Merge branch 'master' into feature_auto_assignment_squashed
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
Build: [](https://circleci.com/gh/eclipse/hawkbit)
|
||||
|
||||
<img src=hawkbit_logo.png width=533 height=246 />
|
||||
|
||||
# Eclipse.IoT hawkBit - Update Server
|
||||
|
||||
[hawkBit](https://projects.eclipse.org/projects/iot.hawkbit) is an domain independent back end solution for rolling out software updates to constrained edge devices as well as more powerful controllers and gateways connected to IP based networking infrastructure.
|
||||
|
||||
Build: [](https://circleci.com/gh/eclipse/hawkbit)
|
||||
[](https://sonar.eu-gb.mybluemix.net)
|
||||
|
||||
# Documentation
|
||||
|
||||
see [hawkBit Wiki](https://github.com/eclipse/hawkbit/wiki)
|
||||
|
||||
@@ -8,6 +8,8 @@ package org.eclipse.hawkbit.app;
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import org.eclipse.hawkbit.ui.HawkbitUI;
|
||||
import org.eclipse.hawkbit.ui.UIEventProvider;
|
||||
import org.eclipse.hawkbit.ui.push.DelayedEventBusPushStrategy;
|
||||
@@ -38,8 +40,8 @@ public class MyUI extends HawkbitUI {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Autowired
|
||||
public MyUI(final EventBus systemEventBus, final org.vaadin.spring.events.EventBus.SessionEventBus eventBus,
|
||||
final UIEventProvider provider) {
|
||||
super(new DelayedEventBusPushStrategy(eventBus, systemEventBus, provider));
|
||||
public MyUI(final ScheduledExecutorService executorService, final EventBus systemEventBus,
|
||||
final org.vaadin.spring.events.EventBus.SessionEventBus eventBus, final UIEventProvider provider) {
|
||||
super(new DelayedEventBusPushStrategy(executorService, eventBus, systemEventBus, provider));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.apache.http.conn.ssl.SSLContextBuilder;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.eclipse.hawkbit.dmf.json.model.Artifact;
|
||||
import org.eclipse.hawkbit.dmf.json.model.Artifact.UrlProtocol;
|
||||
import org.eclipse.hawkbit.dmf.json.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
||||
import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus;
|
||||
@@ -206,12 +205,12 @@ public class DeviceSimulatorUpdater {
|
||||
private static void handleArtifacts(final String targetToken, final List<UpdateStatus> status,
|
||||
final Artifact artifact) {
|
||||
|
||||
if (artifact.getUrls().containsKey(UrlProtocol.HTTPS)) {
|
||||
status.add(downloadUrl(artifact.getUrls().get(UrlProtocol.HTTPS), targetToken,
|
||||
artifact.getHashes().getSha1(), artifact.getSize()));
|
||||
} else if (artifact.getUrls().containsKey(UrlProtocol.HTTP)) {
|
||||
status.add(downloadUrl(artifact.getUrls().get(UrlProtocol.HTTP), targetToken,
|
||||
artifact.getHashes().getSha1(), artifact.getSize()));
|
||||
if (artifact.getUrls().containsKey("HTTPS")) {
|
||||
status.add(downloadUrl(artifact.getUrls().get("HTTPS"), targetToken, artifact.getHashes().getSha1(),
|
||||
artifact.getSize()));
|
||||
} else if (artifact.getUrls().containsKey("HTTP")) {
|
||||
status.add(downloadUrl(artifact.getUrls().get("HTTP"), targetToken, artifact.getHashes().getSha1(),
|
||||
artifact.getSize()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.simulator.amqp;
|
||||
import static org.eclipse.hawkbit.simulator.amqp.AmqpProperties.CONFIGURATION_PREFIX;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -36,6 +35,8 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* The spring AMQP configuration to use a AMQP for communication with SP update
|
||||
* server.
|
||||
@@ -200,13 +201,13 @@ public class AmqpConfiguration {
|
||||
}
|
||||
|
||||
private Map<String, Object> getDeadLetterExchangeArgs() {
|
||||
final Map<String, Object> args = new HashMap<>();
|
||||
final Map<String, Object> args = Maps.newHashMapWithExpectedSize(1);
|
||||
args.put("x-dead-letter-exchange", amqpProperties.getDeadLetterExchange());
|
||||
return args;
|
||||
}
|
||||
|
||||
private static Map<String, Object> getTTLMaxArgs() {
|
||||
final Map<String, Object> args = new HashMap<>();
|
||||
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;
|
||||
|
||||
@@ -206,7 +206,7 @@ public class SpSenderService extends SenderService {
|
||||
headers.put(MessageHeaderKey.TENANT, cacheValue.getTenant());
|
||||
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
||||
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
|
||||
actionUpdateStatus.getMessage().addAll(updateResultMessages);
|
||||
actionUpdateStatus.addMessage(updateResultMessages);
|
||||
actionUpdateStatus.setActionId(cacheValue.getActionId());
|
||||
return convertMessage(actionUpdateStatus, messageProperties);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.app;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import org.eclipse.hawkbit.ui.HawkbitUI;
|
||||
import org.eclipse.hawkbit.ui.UIEventProvider;
|
||||
import org.eclipse.hawkbit.ui.push.DelayedEventBusPushStrategy;
|
||||
@@ -37,8 +39,8 @@ public class MyUI extends HawkbitUI {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Autowired
|
||||
public MyUI(final EventBus systemEventBus, final org.vaadin.spring.events.EventBus.SessionEventBus eventBus,
|
||||
final UIEventProvider provider) {
|
||||
super(new DelayedEventBusPushStrategy(eventBus, systemEventBus, provider));
|
||||
public MyUI(final ScheduledExecutorService scheduledExecutorService, final EventBus systemEventBus,
|
||||
final org.vaadin.spring.events.EventBus.SessionEventBus eventBus, final UIEventProvider provider) {
|
||||
super(new DelayedEventBusPushStrategy(scheduledExecutorService, eventBus, systemEventBus, provider));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,15 @@
|
||||
|
||||
vaadin.servlet.productionMode=true
|
||||
|
||||
hawkbit.artifact.url.coap.enabled=false
|
||||
hawkbit.artifact.url.http.enabled=false
|
||||
hawkbit.artifact.url.https.enabled=true
|
||||
hawkbit.artifact.url.https.pattern={protocol}://{hostname}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}
|
||||
hawkbit.artifact.url.https.hostname=hawkbit.eu-gb.mybluemix.net
|
||||
## Configuration for building download URLs - START
|
||||
hawkbit.artifact.url.protocols.download-http.rel=download-http
|
||||
hawkbit.artifact.url.protocols.download-http.protocol=https
|
||||
hawkbit.artifact.url.protocols.download-http.supports=DMF,DDI
|
||||
hawkbit.artifact.url.protocols.download-http.hostname=hawkbit.eu-gb.mybluemix.net
|
||||
hawkbit.artifact.url.protocols.download-http.ref={protocol}://{hostname}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}
|
||||
hawkbit.artifact.url.protocols.md5sum-http.rel=md5sum-http
|
||||
hawkbit.artifact.url.protocols.md5sum-http.protocol=${hawkbit.artifact.url.protocols.download-http.protocol}
|
||||
hawkbit.artifact.url.protocols.md5sum-http.supports=DDI
|
||||
hawkbit.artifact.url.protocols.md5sum-http.hostname=${hawkbit.artifact.url.protocols.download-http.hostname}
|
||||
hawkbit.artifact.url.protocols.md5sum-http.ref=${hawkbit.artifact.url.protocols.download-http.ref}.MD5SUM
|
||||
## Configuration for building download URLs - END
|
||||
|
||||
@@ -16,12 +16,6 @@ hawkbit.server.ddi.security.authentication.anonymous.enabled=true
|
||||
hawkbit.server.ddi.security.authentication.targettoken.enabled=true
|
||||
hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=true
|
||||
|
||||
# Download URL generation config
|
||||
hawkbit.artifact.url.coap.enabled=false
|
||||
hawkbit.artifact.url.http.enabled=true
|
||||
hawkbit.artifact.url.http.port=8080
|
||||
hawkbit.artifact.url.https.enabled=false
|
||||
|
||||
## Vaadin configuration
|
||||
vaadin.servlet.productionMode=false
|
||||
|
||||
|
||||
@@ -50,11 +50,21 @@ public class SoftwareModuleTypeBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description
|
||||
* of the module
|
||||
* @return the builder itself
|
||||
*/
|
||||
public SoftwareModuleTypeBuilder description(final String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxAssignments
|
||||
* of a module of that type to the same distribution set
|
||||
* @return the builder itself
|
||||
*/
|
||||
public SoftwareModuleTypeBuilder maxAssignments(final int maxAssignments) {
|
||||
this.maxAssignments = maxAssignments;
|
||||
return this;
|
||||
@@ -99,4 +109,4 @@ public class SoftwareModuleTypeBuilder {
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
http://www.eclipse.org/legal/epl-v10.html
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
@@ -22,14 +21,31 @@
|
||||
<name>hawkBit-example :: Parent</name>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>hawkbit-device-simulator</module>
|
||||
<module>hawkbit-example-app</module>
|
||||
<module>hawkbit-example-core-feign-client</module>
|
||||
<module>hawkbit-example-ddi-feign-client</module>
|
||||
<module>hawkbit-example-mgmt-feign-client</module>
|
||||
<module>hawkbit-example-mgmt-simulator</module>
|
||||
</modules>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>noExampleApp</id>
|
||||
<modules>
|
||||
<module>hawkbit-example-core-feign-client</module>
|
||||
<module>hawkbit-example-ddi-feign-client</module>
|
||||
<module>hawkbit-example-mgmt-feign-client</module>
|
||||
<module>hawkbit-example-mgmt-simulator</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>default</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>hawkbit-device-simulator</module>
|
||||
<module>hawkbit-example-app</module>
|
||||
<module>hawkbit-example-core-feign-client</module>
|
||||
<module>hawkbit-example-ddi-feign-client</module>
|
||||
<module>hawkbit-example-mgmt-feign-client</module>
|
||||
<module>hawkbit-example-mgmt-simulator</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
@@ -227,7 +227,7 @@ public class ArtifactStore implements ArtifactRepository {
|
||||
* @return a paged list of artifacts mapped from the given dbFiles
|
||||
*/
|
||||
private List<DbArtifact> map(final List<GridFSDBFile> dbFiles) {
|
||||
return dbFiles.stream().map(this::map).collect(Collectors.toList());
|
||||
return dbFiles.stream().map(ArtifactStore::map).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,7 +263,7 @@ public class ArtifactStore implements ArtifactRepository {
|
||||
* the mongoDB gridFs file.
|
||||
* @return a mapped artifact from the given dbFile
|
||||
*/
|
||||
private GridFsArtifact map(final GridFSFile fsFile) {
|
||||
private static GridFsArtifact map(final GridFSFile fsFile) {
|
||||
if (fsFile == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -82,8 +82,7 @@ public class CacheAutoConfiguration extends CachingConfigurerSupport {
|
||||
*/
|
||||
@Override
|
||||
public Collection<Cache> resolveCaches(final CacheOperationInvocationContext<?> context) {
|
||||
return super.resolveCaches(context).stream().map(cache -> new TenantCacheWrapper(cache))
|
||||
.collect(Collectors.toList());
|
||||
return super.resolveCaches(context).stream().map(TenantCacheWrapper::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -148,7 +148,7 @@ public class SecurityManagedConfiguration {
|
||||
private DdiSecurityProperties ddiSecurityConfiguration;
|
||||
|
||||
@Autowired
|
||||
private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties;
|
||||
private SecurityProperties springSecurityProperties;
|
||||
|
||||
@Autowired
|
||||
private SystemSecurityContext systemSecurityContext;
|
||||
@@ -478,7 +478,7 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends
|
||||
public void onAuthenticationSuccess(final Authentication authentication) throws Exception {
|
||||
|
||||
if (authentication.getClass().equals(TenantUserPasswordAuthenticationToken.class)) {
|
||||
systemSecurityContext.runAsSystemAsTenant(() -> systemManagement.getTenantMetadata(),
|
||||
systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata,
|
||||
((TenantUserPasswordAuthenticationToken) authentication).getTenant().toString());
|
||||
} else if (authentication.getClass().equals(UsernamePasswordAuthenticationToken.class)) {
|
||||
// TODO: vaadin4spring-ext-security does not give us the
|
||||
@@ -489,7 +489,7 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends
|
||||
// vaadin4spring 0.0.7 because it
|
||||
// has been fixed.
|
||||
final String defaultTenant = "DEFAULT";
|
||||
systemSecurityContext.runAsSystemAsTenant(() -> systemManagement.getTenantMetadata(), defaultTenant);
|
||||
systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, defaultTenant);
|
||||
}
|
||||
|
||||
super.onAuthenticationSuccess(authentication);
|
||||
@@ -526,7 +526,7 @@ class AuthenticationSuccessTenantMetadataCreationFilter implements Filter {
|
||||
private void lazyCreateTenantMetadata() {
|
||||
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
systemSecurityContext.runAsSystem(() -> systemManagement.getTenantMetadata());
|
||||
systemSecurityContext.runAsSystem(systemManagement::getTenantMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,10 @@ import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.eclipse.hawkbit.HawkbitServerProperties;
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties;
|
||||
import org.eclipse.hawkbit.api.HostnameResolver;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.eclipse.hawkbit.api.PropertyBasedArtifactUrlHandler;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -22,35 +24,41 @@ import org.springframework.context.annotation.Configuration;
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
/**
|
||||
* Autoconfiguration of the {@link HostnameResolver} based on a property.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Auto configuration for {@link HostnameResolver} and
|
||||
* {@link ArtifactUrlHandler} based on a properties.
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(HawkbitServerProperties.class)
|
||||
@EnableConfigurationProperties({ HawkbitServerProperties.class, ArtifactUrlHandlerProperties.class })
|
||||
public class PropertyHostnameResolverAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private HawkbitServerProperties serverProperties;
|
||||
|
||||
/**
|
||||
* @param serverProperties
|
||||
* to get the servers URL
|
||||
* @return the default autoconfigure hostname resolver implementation which
|
||||
* is property based specified by the property {@link #url}
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = HostnameResolver.class)
|
||||
public HostnameResolver hostnameResolver() {
|
||||
return new HostnameResolver() {
|
||||
@Override
|
||||
public URL resolveHostname() {
|
||||
try {
|
||||
return new URL(serverProperties.getUrl());
|
||||
} catch (final MalformedURLException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
public HostnameResolver hostnameResolver(final HawkbitServerProperties serverProperties) {
|
||||
return () -> {
|
||||
try {
|
||||
return new URL(serverProperties.getUrl());
|
||||
} catch (final MalformedURLException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param urlHandlerProperties
|
||||
* for bean configuration
|
||||
* @return PropertyBasedArtifactUrlHandler bean
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ArtifactUrlHandler.class)
|
||||
public PropertyBasedArtifactUrlHandler propertyBasedArtifactUrlHandler(
|
||||
final ArtifactUrlHandlerProperties urlHandlerProperties) {
|
||||
return new PropertyBasedArtifactUrlHandler(urlHandlerProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,9 +41,25 @@ hawkbit.controller.maxPollingTime=23:59:59
|
||||
hawkbit.controller.minPollingTime=00:00:30
|
||||
# Attention: if you want to use a maximumPollingTime greater 23:59:59 you have to update the DurationField in the configuration window
|
||||
|
||||
|
||||
# Configuration for RabbitMQ integration
|
||||
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter_ttl
|
||||
hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter
|
||||
hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver
|
||||
hawkbit.dmf.rabbitmq.authenticationReceiverQueue=authentication_receiver
|
||||
|
||||
# Download URL generation configuration
|
||||
hawkbit.artifact.url.protocols.download-http.rel=download-http
|
||||
hawkbit.artifact.url.protocols.download-http.hostname=localhost
|
||||
hawkbit.artifact.url.protocols.download-http.ip=127.0.0.1
|
||||
hawkbit.artifact.url.protocols.download-http.protocol=http
|
||||
hawkbit.artifact.url.protocols.download-http.port=8080
|
||||
hawkbit.artifact.url.protocols.download-http.supports=DMF,DDI
|
||||
hawkbit.artifact.url.protocols.download-http.ref={protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}
|
||||
hawkbit.artifact.url.protocols.md5sum-http.rel=md5sum-http
|
||||
hawkbit.artifact.url.protocols.md5sum-http.protocol=${hawkbit.artifact.url.protocols.download-http.protocol}
|
||||
hawkbit.artifact.url.protocols.md5sum-http.hostname=${hawkbit.artifact.url.protocols.download-http.hostname}
|
||||
hawkbit.artifact.url.protocols.md5sum-http.ip=${hawkbit.artifact.url.protocols.download-http.ip}
|
||||
hawkbit.artifact.url.protocols.md5sum-http.port=${hawkbit.artifact.url.protocols.download-http.port}
|
||||
hawkbit.artifact.url.protocols.md5sum-http.supports=DDI
|
||||
hawkbit.artifact.url.protocols.md5sum-http.ref=${hawkbit.artifact.url.protocols.download-http.ref}.MD5SUM
|
||||
|
||||
|
||||
@@ -33,6 +33,16 @@
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.easytesting</groupId>
|
||||
<artifactId>fest-assert-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.easytesting</groupId>
|
||||
<artifactId>fest-assert</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -9,8 +9,18 @@
|
||||
package org.eclipse.hawkbit.api;
|
||||
|
||||
/**
|
||||
* Represented the supported protocols for artifact url's.
|
||||
* hawkBit API type.
|
||||
*
|
||||
*/
|
||||
public enum UrlProtocol {
|
||||
COAP, HTTP, HTTPS
|
||||
public enum ApiType {
|
||||
|
||||
/**
|
||||
* Support for Device Management Federation API.
|
||||
*/
|
||||
DMF,
|
||||
|
||||
/**
|
||||
* Support for Direct Device Integration API.
|
||||
*/
|
||||
DDI;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* 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.api;
|
||||
|
||||
/**
|
||||
* Container for a generated Artifact URL.
|
||||
*
|
||||
*/
|
||||
public class ArtifactUrl {
|
||||
|
||||
private final String protocol;
|
||||
private final String rel;
|
||||
private final String ref;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param protocol
|
||||
* string, e.g. ftp, http, https
|
||||
* @param rel
|
||||
* hypermedia value
|
||||
* @param ref
|
||||
* hypermedia value
|
||||
*/
|
||||
public ArtifactUrl(final String protocol, final String rel, final String ref) {
|
||||
this.protocol = protocol;
|
||||
this.rel = rel;
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return protocol name used in DMF API messages.
|
||||
*/
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return rel links value useful in hypermedia.
|
||||
*/
|
||||
public String getRel() {
|
||||
return rel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return generated artifact download URL
|
||||
*/
|
||||
public String getRef() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((protocol == null) ? 0 : protocol.hashCode());
|
||||
result = prime * result + ((ref == null) ? 0 : ref.hashCode());
|
||||
result = prime * result + ((rel == null) ? 0 : rel.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final ArtifactUrl other = (ArtifactUrl) obj;
|
||||
if (protocol == null) {
|
||||
if (other.protocol != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!protocol.equals(other.protocol)) {
|
||||
return false;
|
||||
}
|
||||
if (ref == null) {
|
||||
if (other.ref != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!ref.equals(other.ref)) {
|
||||
return false;
|
||||
}
|
||||
if (rel == null) {
|
||||
if (other.rel != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!rel.equals(other.rel)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ArtifactUrl [protocol=" + protocol + ", rel=" + rel + ", ref=" + ref + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,36 +8,26 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface declaration of the {@link ArtifactUrlHandler} which generates the
|
||||
* URLs to specific artifacts.
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ArtifactUrlHandler {
|
||||
|
||||
/**
|
||||
* Returns a generated download URL for a given artifact parameters for a
|
||||
* specific protocol.
|
||||
*
|
||||
* @param controllerId
|
||||
* the authenticated controller id
|
||||
* @param softwareModuleId
|
||||
* the softwareModuleId belonging to the artifact
|
||||
* @param filename
|
||||
* the filename of the artifact
|
||||
* @param sha1Hash
|
||||
* the sha1Hash of the artifact
|
||||
* @param protocol
|
||||
* the protocol the URL should be generated
|
||||
* @param placeholder
|
||||
* data for URL generation
|
||||
* @param api
|
||||
* given protocol that URL needs to support
|
||||
*
|
||||
* @return an URL for the given artifact parameters in a given protocol
|
||||
*/
|
||||
String getUrl(String controllerId, final Long softwareModuleId, final String filename, final String sha1Hash,
|
||||
final UrlProtocol protocol);
|
||||
|
||||
/**
|
||||
* @param protocol
|
||||
* to check support for
|
||||
* @return <code>true</code> of the handler supports given protocol.
|
||||
*/
|
||||
boolean protocolSupported(UrlProtocol protocol);
|
||||
List<ArtifactUrl> getUrls(URLPlaceholder placeholder, ApiType api);
|
||||
}
|
||||
|
||||
@@ -8,90 +8,153 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.api;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* Artifact handler properties class for holding all supported protocols with
|
||||
* host, ip, port and download pattern.
|
||||
*
|
||||
* @see PropertyBasedArtifactUrlHandler
|
||||
*/
|
||||
@ConfigurationProperties("hawkbit.artifact.url")
|
||||
public class ArtifactUrlHandlerProperties {
|
||||
|
||||
private final Http http = new Http();
|
||||
private final Https https = new Https();
|
||||
private final Coap coap = new Coap();
|
||||
|
||||
public Http getHttp() {
|
||||
return http;
|
||||
}
|
||||
|
||||
public Https getHttps() {
|
||||
return https;
|
||||
}
|
||||
|
||||
public Coap getCoap() {
|
||||
return coap;
|
||||
}
|
||||
/**
|
||||
* Rel as key and complete protocol as value.
|
||||
*/
|
||||
private final Map<String, UrlProtocol> protocols = new HashMap<>();
|
||||
|
||||
/**
|
||||
* @param protocol
|
||||
* the protocol schema to retrieve the properties.
|
||||
* @return the properties to a protocol or {@code null} if protocol does not
|
||||
* have properties or protocol not supported
|
||||
* Protocol specific properties to generate URLs accordingly.
|
||||
*
|
||||
*/
|
||||
public ProtocolProperties getProperties(final String protocol) {
|
||||
switch (protocol) {
|
||||
case "http":
|
||||
return getHttp();
|
||||
case "https":
|
||||
return getHttps();
|
||||
case "coap":
|
||||
return getCoap();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static class UrlProtocol {
|
||||
|
||||
/**
|
||||
* Object to hold the properties for the HTTP protocol.
|
||||
*/
|
||||
public static class Http extends DefaultProtocolProperties {
|
||||
private static final int DEFAULT_HTTP_PORT = 8080;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Set to true if enabled.
|
||||
*/
|
||||
public Http() {
|
||||
setPattern(
|
||||
"{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Object to hold the properties for the HTTP protocol.
|
||||
*/
|
||||
public static class Https extends DefaultProtocolProperties {
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Hypermedia rel value for this protocol.
|
||||
*/
|
||||
public Https() {
|
||||
setPattern(
|
||||
"{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Object to hold the properties for the HTTP protocol.
|
||||
*/
|
||||
public static class Coap extends DefaultProtocolProperties {
|
||||
private String rel = "download-http";
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Hypermedia ref pattern for this protocol. Supported place holders are
|
||||
* protocol,controllerId,targetId,targetIdBase62,ip,port,hostname,
|
||||
* artifactFileName,artifactSHA1,
|
||||
* artifactIdBase62,artifactId,tenant,softwareModuleId,
|
||||
* softwareModuleIdBase62.
|
||||
*
|
||||
* The update server itself supports
|
||||
*/
|
||||
public Coap() {
|
||||
setPattern("{protocol}://{ip}:{port}/fw/{tenant}/{targetId}/sha1/{artifactSHA1}");
|
||||
setPort("5683");
|
||||
private String ref = "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}";
|
||||
|
||||
/**
|
||||
* Protocol name placeholder that can be used in ref pattern.
|
||||
*/
|
||||
private String protocol = "http";
|
||||
|
||||
/**
|
||||
* Hostname placeholder that can be used in ref pattern.
|
||||
*/
|
||||
private String hostname = "localhost";
|
||||
|
||||
/**
|
||||
* IP address placeholder that can be used in ref pattern.
|
||||
*/
|
||||
// Exception squid:S1313 - default only, can be configured
|
||||
@SuppressWarnings("squid:S1313")
|
||||
private String ip = "127.0.0.1";
|
||||
|
||||
/**
|
||||
* Port placeholder that can be used in ref pattern.
|
||||
*/
|
||||
private Integer port = DEFAULT_HTTP_PORT;
|
||||
|
||||
/**
|
||||
* Support for the following hawkBit API.
|
||||
*/
|
||||
private List<ApiType> supports = Lists.newArrayList(ApiType.DDI, ApiType.DMF);
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(final boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getRel() {
|
||||
return rel;
|
||||
}
|
||||
|
||||
public void setRel(final String rel) {
|
||||
this.rel = rel;
|
||||
}
|
||||
|
||||
public String getRef() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public void setRef(final String ref) {
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public void setHostname(final String hostname) {
|
||||
this.hostname = hostname;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(final String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(final Integer port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public List<ApiType> getSupports() {
|
||||
return Collections.unmodifiableList(supports);
|
||||
}
|
||||
|
||||
public void setSupports(final List<ApiType> supports) {
|
||||
this.supports = Collections.unmodifiableList(supports);
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(final String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Map<String, UrlProtocol> getProtocols() {
|
||||
return protocols;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 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.api;
|
||||
|
||||
/**
|
||||
* Utility class for Base10 to Base62 conversion and vice versa. Base62 has the
|
||||
* benefit of being shorter in ASCII representation than Base10.
|
||||
*/
|
||||
public final class Base62Util {
|
||||
private static final String BASE62_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
private static final int BASE62_BASE = BASE62_ALPHABET.length();
|
||||
|
||||
private Base62Util() {
|
||||
// Utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* @param base10
|
||||
* number
|
||||
* @return converted number into Base62 ASCII string
|
||||
*/
|
||||
public static String fromBase10(final long base10) {
|
||||
if (base10 == 0) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
long temp = base10;
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
while (temp > 0) {
|
||||
temp = fromBase10(temp, sb);
|
||||
}
|
||||
return sb.reverse().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param base62
|
||||
* number
|
||||
* @return converted number into Base10
|
||||
*/
|
||||
public static Long toBase10(final String base62) {
|
||||
return toBase10(new StringBuilder(base62).reverse().toString().toCharArray());
|
||||
}
|
||||
|
||||
private static Long fromBase10(final long base10, final StringBuilder sb) {
|
||||
final int rem = (int) (base10 % BASE62_BASE);
|
||||
sb.append(BASE62_ALPHABET.charAt(rem));
|
||||
return base10 / BASE62_BASE;
|
||||
}
|
||||
|
||||
private static Long toBase10(final char[] chars) {
|
||||
long base10 = 0L;
|
||||
for (int i = chars.length - 1; i >= 0; i--) {
|
||||
base10 += toBase10(BASE62_ALPHABET.indexOf(chars[i]), i);
|
||||
}
|
||||
return base10;
|
||||
}
|
||||
|
||||
private static int toBase10(final int n, final int pow) {
|
||||
return n * (int) Math.pow(BASE62_BASE, pow);
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/**
|
||||
* 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.api;
|
||||
|
||||
/**
|
||||
* Object to hold the properties for the base protocols.
|
||||
*/
|
||||
public class DefaultProtocolProperties implements ProtocolProperties {
|
||||
// The IP address is not hardcoded. It's the default value, if the IP
|
||||
// address is not configured.
|
||||
@SuppressWarnings("squid:S1313")
|
||||
private static final String DEFAULT_IP_LOCALHOST = "127.0.0.1";
|
||||
private static final String LOCALHOST = "localhost";
|
||||
|
||||
private String hostname = LOCALHOST;
|
||||
private String ip = DEFAULT_IP_LOCALHOST;
|
||||
private String port = "";
|
||||
/**
|
||||
* An ant-URL pattern with placeholder to build the URL on. The URL can have
|
||||
* specific artifact placeholder.
|
||||
*/
|
||||
private String pattern;
|
||||
|
||||
/**
|
||||
* Enables protocol.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(final boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public void setHostname(final String hostname) {
|
||||
this.hostname = hostname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(final String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public void setPattern(final String urlPattern) {
|
||||
this.pattern = urlPattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(final String port) {
|
||||
this.port = port;
|
||||
}
|
||||
}
|
||||
@@ -9,55 +9,80 @@
|
||||
package org.eclipse.hawkbit.api;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties.UrlProtocol;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.net.UrlEscapers;
|
||||
|
||||
/**
|
||||
* Implementation for ArtifactUrlHandler for creating urls to download resource
|
||||
* based on pattern.
|
||||
* based on patterns configured by {@link ArtifactUrlHandlerProperties}.
|
||||
*
|
||||
* This mechanism can be used to generate links to arbitrary file hosting
|
||||
* infrastructure. However, the hawkBit update server supports hosting files as
|
||||
* well in the following {@link UrlProtocol#getRef()} patterns:
|
||||
*
|
||||
* Default:
|
||||
* {protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/
|
||||
* softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}
|
||||
*
|
||||
* Default (MD5SUM files):
|
||||
* {protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/
|
||||
* softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}.MD5SUM
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
@EnableConfigurationProperties(ArtifactUrlHandlerProperties.class)
|
||||
public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler {
|
||||
|
||||
private static final String PROTOCOL_PLACEHOLDER = "protocol";
|
||||
private static final String TARGET_ID_PLACEHOLDER = "targetId";
|
||||
private static final String CONTROLLER_ID_PLACEHOLDER = "controllerId";
|
||||
private static final String TARGET_ID_BASE10_PLACEHOLDER = "targetId";
|
||||
private static final String TARGET_ID_BASE62_PLACEHOLDER = "targetIdBase62";
|
||||
private static final String IP_PLACEHOLDER = "ip";
|
||||
private static final String PORT_PLACEHOLDER = "port";
|
||||
private static final String HOSTNAME_PLACEHOLDER = "hostname";
|
||||
private static final String ARTIFACT_FILENAME_PLACEHOLDER = "artifactFileName";
|
||||
private static final String ARTIFACT_SHA1_PLACEHOLDER = "artifactSHA1";
|
||||
private static final String ARTIFACT_ID_BASE10_PLACEHOLDER = "artifactId";
|
||||
private static final String ARTIFACT_ID_BASE62_PLACEHOLDER = "artifactIdBase62";
|
||||
private static final String TENANT_PLACEHOLDER = "tenant";
|
||||
private static final String SOFTWARE_MODULE_ID_PLACDEHOLDER = "softwareModuleId";
|
||||
private static final String TENANT_ID_BASE10_PLACEHOLDER = "tenantId";
|
||||
private static final String TENANT_ID_BASE62_PLACEHOLDER = "tenantIdBase62";
|
||||
private static final String SOFTWARE_MODULE_ID_BASE10_PLACDEHOLDER = "softwareModuleId";
|
||||
private static final String SOFTWARE_MODULE_ID_BASE62_PLACDEHOLDER = "softwareModuleIdBase62";
|
||||
|
||||
@Autowired
|
||||
private ArtifactUrlHandlerProperties urlHandlerProperties;
|
||||
private final ArtifactUrlHandlerProperties urlHandlerProperties;
|
||||
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
/**
|
||||
* @param urlHandlerProperties
|
||||
* for URL generation configuration
|
||||
*/
|
||||
public PropertyBasedArtifactUrlHandler(final ArtifactUrlHandlerProperties urlHandlerProperties) {
|
||||
this.urlHandlerProperties = urlHandlerProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl(final String targetId, final Long softwareModuleId, final String filename,
|
||||
final String sha1Hash, final UrlProtocol protocol) {
|
||||
public List<ArtifactUrl> getUrls(final URLPlaceholder placeholder, final ApiType api) {
|
||||
|
||||
final String protocolString = protocol.name().toLowerCase();
|
||||
final ProtocolProperties properties = urlHandlerProperties.getProperties(protocolString);
|
||||
if (properties == null || properties.getPattern() == null) {
|
||||
return null;
|
||||
}
|
||||
return urlHandlerProperties.getProtocols().entrySet().stream()
|
||||
.filter(entry -> entry.getValue().getSupports().contains(api))
|
||||
.filter(entry -> entry.getValue().isEnabled())
|
||||
.map(entry -> new ArtifactUrl(entry.getValue().getProtocol().toUpperCase(), entry.getValue().getRel(),
|
||||
generateUrl(entry.getValue(), placeholder)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
private static String generateUrl(final UrlProtocol protocol, final URLPlaceholder placeholder) {
|
||||
final Set<Entry<String, String>> entrySet = getReplaceMap(protocol, placeholder).entrySet();
|
||||
|
||||
String urlPattern = protocol.getRef();
|
||||
|
||||
String urlPattern = properties.getPattern();
|
||||
final Set<Entry<String, String>> entrySet = getReplaceMap(targetId, softwareModuleId,
|
||||
UrlEscapers.urlFragmentEscaper().escape(filename), sha1Hash, protocolString, properties).entrySet();
|
||||
for (final Entry<String, String> entry : entrySet) {
|
||||
if (entry.getKey().equals(PORT_PLACEHOLDER)) {
|
||||
urlPattern = urlPattern.replace(":{" + entry.getKey() + "}",
|
||||
@@ -69,30 +94,29 @@ public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler {
|
||||
return urlPattern;
|
||||
}
|
||||
|
||||
private Map<String, String> getReplaceMap(final String targetId, final Long softwareModuleId, final String filename,
|
||||
final String sha1Hash, final String protocol, final ProtocolProperties properties) {
|
||||
private static Map<String, String> getReplaceMap(final UrlProtocol protocol, final URLPlaceholder placeholder) {
|
||||
final Map<String, String> replaceMap = new HashMap<>();
|
||||
replaceMap.put(IP_PLACEHOLDER, properties.getIp());
|
||||
replaceMap.put(HOSTNAME_PLACEHOLDER, properties.getHostname());
|
||||
replaceMap.put(ARTIFACT_FILENAME_PLACEHOLDER, filename);
|
||||
replaceMap.put(ARTIFACT_SHA1_PLACEHOLDER, sha1Hash);
|
||||
replaceMap.put(PROTOCOL_PLACEHOLDER, protocol);
|
||||
replaceMap.put(PORT_PLACEHOLDER, properties.getPort());
|
||||
replaceMap.put(TENANT_PLACEHOLDER, tenantAware.getCurrentTenant());
|
||||
replaceMap.put(TARGET_ID_PLACEHOLDER, targetId);
|
||||
replaceMap.put(SOFTWARE_MODULE_ID_PLACDEHOLDER, String.valueOf(softwareModuleId));
|
||||
replaceMap.put(IP_PLACEHOLDER, protocol.getIp());
|
||||
replaceMap.put(HOSTNAME_PLACEHOLDER, protocol.getHostname());
|
||||
replaceMap.put(ARTIFACT_FILENAME_PLACEHOLDER,
|
||||
UrlEscapers.urlFragmentEscaper().escape(placeholder.getSoftwareData().getFilename()));
|
||||
replaceMap.put(ARTIFACT_SHA1_PLACEHOLDER, placeholder.getSoftwareData().getSha1Hash());
|
||||
replaceMap.put(PROTOCOL_PLACEHOLDER, protocol.getProtocol());
|
||||
replaceMap.put(PORT_PLACEHOLDER, protocol.getPort() == null ? null : String.valueOf(protocol.getPort()));
|
||||
replaceMap.put(TENANT_PLACEHOLDER, placeholder.getTenant());
|
||||
replaceMap.put(TENANT_ID_BASE10_PLACEHOLDER, String.valueOf(placeholder.getTenantId()));
|
||||
replaceMap.put(TENANT_ID_BASE62_PLACEHOLDER, Base62Util.fromBase10(placeholder.getTenantId()));
|
||||
replaceMap.put(CONTROLLER_ID_PLACEHOLDER, placeholder.getControllerId());
|
||||
replaceMap.put(TARGET_ID_BASE10_PLACEHOLDER, String.valueOf(placeholder.getTargetId()));
|
||||
replaceMap.put(TARGET_ID_BASE62_PLACEHOLDER, Base62Util.fromBase10(placeholder.getTargetId()));
|
||||
replaceMap.put(ARTIFACT_ID_BASE62_PLACEHOLDER,
|
||||
Base62Util.fromBase10(placeholder.getSoftwareData().getArtifactId()));
|
||||
replaceMap.put(ARTIFACT_ID_BASE10_PLACEHOLDER, String.valueOf(placeholder.getSoftwareData().getArtifactId()));
|
||||
replaceMap.put(SOFTWARE_MODULE_ID_BASE10_PLACDEHOLDER,
|
||||
String.valueOf(placeholder.getSoftwareData().getSoftwareModuleId()));
|
||||
replaceMap.put(SOFTWARE_MODULE_ID_BASE62_PLACDEHOLDER,
|
||||
Base62Util.fromBase10(placeholder.getSoftwareData().getSoftwareModuleId()));
|
||||
return replaceMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean protocolSupported(final UrlProtocol protocol) {
|
||||
final String protocolString = protocol.name().toLowerCase();
|
||||
final ProtocolProperties properties = urlHandlerProperties.getProperties(protocolString);
|
||||
if (properties == null || properties.getPattern() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return properties.isEnabled();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* 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.api;
|
||||
|
||||
/**
|
||||
* Container for variables available to the {@link ArtifactUrlHandler}.
|
||||
*
|
||||
*/
|
||||
public class URLPlaceholder {
|
||||
private final String tenant;
|
||||
private final Long tenantId;
|
||||
private final String controllerId;
|
||||
private final Long targetId;
|
||||
private final SoftwareData softwareData;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param tenant
|
||||
* of the client
|
||||
* @param tenantId
|
||||
* of teh tenant
|
||||
* @param controllerId
|
||||
* of the target
|
||||
* @param targetId
|
||||
* of the target
|
||||
* @param softwareData
|
||||
* information about the artifact and software module that can be
|
||||
* accessed by the URL.
|
||||
*/
|
||||
public URLPlaceholder(final String tenant, final Long tenantId, final String controllerId, final Long targetId,
|
||||
final SoftwareData softwareData) {
|
||||
this.tenant = tenant;
|
||||
this.tenantId = tenantId;
|
||||
this.controllerId = controllerId;
|
||||
this.targetId = targetId;
|
||||
this.softwareData = softwareData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about the artifact and software module that can be accessed
|
||||
* by the URL.
|
||||
*
|
||||
*/
|
||||
public static class SoftwareData {
|
||||
private Long softwareModuleId;
|
||||
private String filename;
|
||||
private Long artifactId;
|
||||
private String sha1Hash;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param softwareModuleId
|
||||
* of the module the artifact belongs to
|
||||
* @param filename
|
||||
* of the artifact
|
||||
* @param artifactId
|
||||
* of the artifact
|
||||
* @param sha1Hash
|
||||
* of the artifact
|
||||
*/
|
||||
public SoftwareData(final Long softwareModuleId, final String filename, final Long artifactId,
|
||||
final String sha1Hash) {
|
||||
this.softwareModuleId = softwareModuleId;
|
||||
this.filename = filename;
|
||||
this.artifactId = artifactId;
|
||||
this.sha1Hash = sha1Hash;
|
||||
}
|
||||
|
||||
public Long getSoftwareModuleId() {
|
||||
return softwareModuleId;
|
||||
}
|
||||
|
||||
public void setSoftwareModuleId(final Long softwareModuleId) {
|
||||
this.softwareModuleId = softwareModuleId;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public void setFilename(final String filename) {
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public Long getArtifactId() {
|
||||
return artifactId;
|
||||
}
|
||||
|
||||
public void setArtifactId(final Long artifactId) {
|
||||
this.artifactId = artifactId;
|
||||
}
|
||||
|
||||
public String getSha1Hash() {
|
||||
return sha1Hash;
|
||||
}
|
||||
|
||||
public void setSha1Hash(final String sha1Hash) {
|
||||
this.sha1Hash = sha1Hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((artifactId == null) ? 0 : artifactId.hashCode());
|
||||
result = prime * result + ((filename == null) ? 0 : filename.hashCode());
|
||||
result = prime * result + ((sha1Hash == null) ? 0 : sha1Hash.hashCode());
|
||||
result = prime * result + ((softwareModuleId == null) ? 0 : softwareModuleId.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final SoftwareData other = (SoftwareData) obj;
|
||||
if (artifactId == null) {
|
||||
if (other.artifactId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!artifactId.equals(other.artifactId)) {
|
||||
return false;
|
||||
}
|
||||
if (filename == null) {
|
||||
if (other.filename != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!filename.equals(other.filename)) {
|
||||
return false;
|
||||
}
|
||||
if (sha1Hash == null) {
|
||||
if (other.sha1Hash != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!sha1Hash.equals(other.sha1Hash)) {
|
||||
return false;
|
||||
}
|
||||
if (softwareModuleId == null) {
|
||||
if (other.softwareModuleId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!softwareModuleId.equals(other.softwareModuleId)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getTenant() {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public String getControllerId() {
|
||||
return controllerId;
|
||||
}
|
||||
|
||||
public Long getTargetId() {
|
||||
return targetId;
|
||||
}
|
||||
|
||||
public SoftwareData getSoftwareData() {
|
||||
return softwareData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((controllerId == null) ? 0 : controllerId.hashCode());
|
||||
result = prime * result + ((softwareData == null) ? 0 : softwareData.hashCode());
|
||||
result = prime * result + ((targetId == null) ? 0 : targetId.hashCode());
|
||||
result = prime * result + ((tenant == null) ? 0 : tenant.hashCode());
|
||||
result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final URLPlaceholder other = (URLPlaceholder) obj;
|
||||
if (controllerId == null) {
|
||||
if (other.controllerId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!controllerId.equals(other.controllerId)) {
|
||||
return false;
|
||||
}
|
||||
if (softwareData == null) {
|
||||
if (other.softwareData != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!softwareData.equals(other.softwareData)) {
|
||||
return false;
|
||||
}
|
||||
if (targetId == null) {
|
||||
if (other.targetId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!targetId.equals(other.targetId)) {
|
||||
return false;
|
||||
}
|
||||
if (tenant == null) {
|
||||
if (other.tenant != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!tenant.equals(other.tenant)) {
|
||||
return false;
|
||||
}
|
||||
if (tenantId == null) {
|
||||
if (other.tenantId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!tenantId.equals(other.tenantId)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -90,7 +90,7 @@ public enum TenantConfigurationKey {
|
||||
* @param validator
|
||||
* Validator which validates, that property is of correct format
|
||||
*/
|
||||
private TenantConfigurationKey(final String key, final String defaultKeyName, final Class<?> dataType,
|
||||
TenantConfigurationKey(final String key, final String defaultKeyName, final Class<?> dataType,
|
||||
final String defaultValue, final Class<? extends TenantConfigurationValidator> validator) {
|
||||
this.keyName = key;
|
||||
this.dataType = dataType;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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.api;
|
||||
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
@Features("Unit Tests - Artifact URL Handler")
|
||||
@Stories("Base62 Utility tests")
|
||||
public class Base62UtilTest {
|
||||
|
||||
@Test
|
||||
@Description("Convert Base10 numbres to Base62 ASCII strings.")
|
||||
public void fromBase10() {
|
||||
assertThat(Base62Util.fromBase10(0L)).isEqualTo("0");
|
||||
assertThat(Base62Util.fromBase10(11L)).isEqualTo("B");
|
||||
assertThat(Base62Util.fromBase10(36L)).isEqualTo("a");
|
||||
assertThat(Base62Util.fromBase10(999L)).isEqualTo("G7");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Convert Base62 ASCII strings to Base10 numbers.")
|
||||
public void toBase10() {
|
||||
assertThat(Base62Util.toBase10("0")).isEqualTo(0L);
|
||||
assertThat(Base62Util.toBase10("B")).isEqualTo(11);
|
||||
assertThat(Base62Util.toBase10("a")).isEqualTo(36L);
|
||||
assertThat(Base62Util.toBase10("G7")).isEqualTo(999L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* 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.api;
|
||||
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties.UrlProtocol;
|
||||
import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
/**
|
||||
* Tests for creating urls to download artifacts.
|
||||
*/
|
||||
@Features("Unit Tests - Artifact URL Handler")
|
||||
@Stories("Test to generate the artifact download URL")
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PropertyBasedArtifactUrlHandlerTest {
|
||||
|
||||
private static final String TEST_PROTO = "coap";
|
||||
private static final String TEST_REL = "download-udp";
|
||||
|
||||
private static final long TENANT_ID = 456789L;
|
||||
private static final String CONTROLLER_ID = "Test";
|
||||
private static final String FILENAME = "Afile1234";
|
||||
private static final long SOFTWAREMODULEID = 87654L;
|
||||
private static final long TARGETID = 3474366L;
|
||||
private static final String TARGETID_BASE62 = "EZqA";
|
||||
private static final String SHA1HASH = "test12345";
|
||||
private static final long ARTIFACTID = 1345678L;
|
||||
private static final String ARTIFACTID_BASE62 = "5e4U";
|
||||
private static final String TENANT = "TEST_TENANT";
|
||||
|
||||
private static final String HTTP_LOCALHOST = "http://localhost:8080/";
|
||||
|
||||
private ArtifactUrlHandler urlHandlerUnderTest;
|
||||
|
||||
private ArtifactUrlHandlerProperties properties;
|
||||
|
||||
private static URLPlaceholder placeholder = new URLPlaceholder(TENANT, TENANT_ID, CONTROLLER_ID, TARGETID,
|
||||
new SoftwareData(SOFTWAREMODULEID, FILENAME, ARTIFACTID, SHA1HASH));
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
properties = new ArtifactUrlHandlerProperties();
|
||||
urlHandlerUnderTest = new PropertyBasedArtifactUrlHandler(properties);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests the generation of http download url.")
|
||||
public void urlGenerationWithDefaultConfiguration() {
|
||||
properties.getProtocols().put("download-http", new UrlProtocol());
|
||||
|
||||
final List<ArtifactUrl> ddiUrls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DDI);
|
||||
assertEquals(Lists.newArrayList(
|
||||
new ArtifactUrl("http".toUpperCase(), "download-http", HTTP_LOCALHOST + TENANT + "/controller/v1/"
|
||||
+ CONTROLLER_ID + "/softwaremodules/" + SOFTWAREMODULEID + "/artifacts/" + FILENAME)),
|
||||
ddiUrls);
|
||||
|
||||
final List<ArtifactUrl> dmfUrls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DMF);
|
||||
assertEquals(ddiUrls, dmfUrls);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests the generation of custom download url with a CoAP example that supports DMF only.")
|
||||
public void urlGenerationWithCustomConfiguration() {
|
||||
final UrlProtocol proto = new UrlProtocol();
|
||||
proto.setIp("127.0.0.1");
|
||||
proto.setPort(5683);
|
||||
proto.setProtocol(TEST_PROTO);
|
||||
proto.setRel(TEST_REL);
|
||||
proto.setSupports(Lists.newArrayList(ApiType.DMF));
|
||||
proto.setRef("{protocol}://{ip}:{port}/fw/{tenant}/{controllerId}/sha1/{artifactSHA1}");
|
||||
properties.getProtocols().put(TEST_PROTO, proto);
|
||||
|
||||
List<ArtifactUrl> urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DDI);
|
||||
|
||||
assertThat(urls).isEmpty();
|
||||
urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DMF);
|
||||
|
||||
assertEquals(Lists.newArrayList(new ArtifactUrl(TEST_PROTO.toUpperCase(), TEST_REL,
|
||||
"coap://127.0.0.1:5683/fw/" + TENANT + "/" + CONTROLLER_ID + "/sha1/" + SHA1HASH)), urls);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests the generation of custom download url using Base62 references with a CoAP example that supports DMF only.")
|
||||
public void urlGenerationWithCustomShortConfiguration() {
|
||||
final UrlProtocol proto = new UrlProtocol();
|
||||
proto.setIp("127.0.0.1");
|
||||
proto.setPort(5683);
|
||||
proto.setProtocol(TEST_PROTO);
|
||||
proto.setRel(TEST_REL);
|
||||
proto.setSupports(Lists.newArrayList(ApiType.DMF));
|
||||
proto.setRef("{protocol}://{ip}:{port}/fws/{tenant}/{targetIdBase62}/{artifactIdBase62}");
|
||||
properties.getProtocols().put("ftp", proto);
|
||||
|
||||
List<ArtifactUrl> urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DDI);
|
||||
|
||||
assertThat(urls).isEmpty();
|
||||
urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DMF);
|
||||
|
||||
assertEquals(Lists.newArrayList(new ArtifactUrl(TEST_PROTO.toUpperCase(), TEST_REL,
|
||||
TEST_PROTO + "://127.0.0.1:5683/fws/" + TENANT + "/" + TARGETID_BASE62 + "/" + ARTIFACTID_BASE62)),
|
||||
urls);
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ public class DdiCancelActionToStop {
|
||||
* ID of the action to be stoppedW
|
||||
*/
|
||||
public DdiCancelActionToStop(final String stopId) {
|
||||
super();
|
||||
this.stopId = stopId;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ddi.json.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
@@ -65,7 +66,11 @@ public class DdiChunk {
|
||||
}
|
||||
|
||||
public List<DdiArtifact> getArtifacts() {
|
||||
return artifacts;
|
||||
if (artifacts == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(artifacts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ public class DdiConfig {
|
||||
* configuration of the SP target
|
||||
*/
|
||||
public DdiConfig(final DdiPolling polling) {
|
||||
super();
|
||||
this.polling = polling;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ddi.json.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
@@ -41,7 +42,6 @@ public class DdiDeployment {
|
||||
* to handle.
|
||||
*/
|
||||
public DdiDeployment(final HandlingType download, final HandlingType update, final List<DdiChunk> chunks) {
|
||||
super();
|
||||
this.download = download;
|
||||
this.update = update;
|
||||
this.chunks = chunks;
|
||||
@@ -56,7 +56,11 @@ public class DdiDeployment {
|
||||
}
|
||||
|
||||
public List<DdiChunk> getChunks() {
|
||||
return chunks;
|
||||
if (chunks == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(chunks);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +85,7 @@ public class DdiDeployment {
|
||||
|
||||
private String name;
|
||||
|
||||
private HandlingType(final String name) {
|
||||
HandlingType(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ public class DdiResult {
|
||||
|
||||
private String name;
|
||||
|
||||
private FinalResult(final String name) {
|
||||
FinalResult(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ddi.json.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
@@ -57,7 +58,11 @@ public class DdiStatus {
|
||||
}
|
||||
|
||||
public List<String> getDetails() {
|
||||
return details;
|
||||
if (details == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(details);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +103,7 @@ public class DdiStatus {
|
||||
|
||||
private String name;
|
||||
|
||||
private ExecutionStatus(final String name) {
|
||||
ExecutionStatus(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,39 +37,45 @@ public interface DdiRootControllerRestApi {
|
||||
|
||||
/**
|
||||
* Returns all artifacts of a given software module and target.
|
||||
*
|
||||
* @param targetid
|
||||
*
|
||||
* @param tenant
|
||||
* of the client
|
||||
* @param controllerId
|
||||
* of the target that matches to controller id
|
||||
* @param softwareModuleId
|
||||
* of the software module
|
||||
* @return the response
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetid}/softwaremodules/{softwareModuleId}/artifacts", produces = {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts", produces = {
|
||||
"application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<List<org.eclipse.hawkbit.ddi.json.model.DdiArtifact>> getSoftwareModulesArtifacts(
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("targetid") final String targetid,
|
||||
ResponseEntity<List<DdiArtifact>> getSoftwareModulesArtifacts(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("softwareModuleId") final Long softwareModuleId);
|
||||
|
||||
/**
|
||||
* Root resource for an individual {@link Target}.
|
||||
*
|
||||
* @param targetid
|
||||
* @param tenant
|
||||
* of the request
|
||||
* @param controllerId
|
||||
* of the target that matches to controller id
|
||||
* @param request
|
||||
* the HTTP request injected by spring
|
||||
* @return the response
|
||||
*/
|
||||
@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<DdiControllerBase> getControllerBase(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("targetid") final String targetid);
|
||||
@PathVariable("controllerId") final String controllerId);
|
||||
|
||||
/**
|
||||
* Handles GET {@link DdiArtifact} download request. This could be full or
|
||||
* partial (as specified by RFC7233 (Range Requests)) download request.
|
||||
*
|
||||
* @param targetid
|
||||
* of the related target
|
||||
* @param tenant
|
||||
* of the request
|
||||
* @param controllerId
|
||||
* of the target
|
||||
* @param softwareModuleId
|
||||
* of the parent software module
|
||||
* @param fileName
|
||||
@@ -83,17 +89,19 @@ public interface DdiRootControllerRestApi {
|
||||
* {@link HttpStatus#OK} or in case of partial download
|
||||
* {@link HttpStatus#PARTIAL_CONTENT}.
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetid}/softwaremodules/{softwareModuleId}/artifacts/{fileName}")
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName}")
|
||||
ResponseEntity<InputStream> downloadArtifact(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("targetid") final String targetid,
|
||||
@PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||
@PathVariable("fileName") final String fileName);
|
||||
|
||||
/**
|
||||
* Handles GET {@link DdiArtifact} MD5 checksum file download request.
|
||||
*
|
||||
* @param targetid
|
||||
* of the related target
|
||||
* @param tenant
|
||||
* of the request
|
||||
* @param controllerId
|
||||
* of the target
|
||||
* @param softwareModuleId
|
||||
* of the parent software module
|
||||
* @param fileName
|
||||
@@ -106,18 +114,20 @@ public interface DdiRootControllerRestApi {
|
||||
* @return {@link ResponseEntity} with status {@link HttpStatus#OK} if
|
||||
* successful
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetid}/softwaremodules/{softwareModuleId}/artifacts/{fileName}"
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName}"
|
||||
+ DdiRestConstants.ARTIFACT_MD5_DWNL_SUFFIX, produces = MediaType.TEXT_PLAIN_VALUE)
|
||||
ResponseEntity<Void> downloadArtifactMd5(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("targetid") final String targetid,
|
||||
@PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||
@PathVariable("fileName") final String fileName);
|
||||
|
||||
/**
|
||||
* Resource for software module.
|
||||
*
|
||||
* @param targetid
|
||||
* of the target that matches to controller id
|
||||
* @param tenant
|
||||
* of the request
|
||||
* @param controllerId
|
||||
* of the target
|
||||
* @param actionId
|
||||
* of the {@link DdiDeploymentBase} that matches to active
|
||||
* actions.
|
||||
@@ -129,19 +139,21 @@ public interface DdiRootControllerRestApi {
|
||||
* the HTTP request injected by spring
|
||||
* @return the response
|
||||
*/
|
||||
@RequestMapping(value = "/{targetid}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION
|
||||
@RequestMapping(value = "/{controllerId}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION
|
||||
+ "/{actionId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
ResponseEntity<DdiDeploymentBase> getControllerBasedeploymentAction(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("targetid") @NotEmpty final String targetid,
|
||||
@PathVariable("controllerId") @NotEmpty final String controllerId,
|
||||
@PathVariable("actionId") @NotEmpty final Long actionId,
|
||||
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource);
|
||||
|
||||
/**
|
||||
* This is the feedback channel for the {@link DdiDeploymentBase} action.
|
||||
*
|
||||
* @param tenant
|
||||
* of the client
|
||||
* @param feedback
|
||||
* to provide
|
||||
* @param targetid
|
||||
* @param controllerId
|
||||
* of the target that matches to controller id
|
||||
* @param actionId
|
||||
* of the action we have feedback for
|
||||
@@ -150,33 +162,37 @@ public interface DdiRootControllerRestApi {
|
||||
*
|
||||
* @return the response
|
||||
*/
|
||||
@RequestMapping(value = "/{targetid}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}/"
|
||||
@RequestMapping(value = "/{controllerId}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}/"
|
||||
+ DdiRestConstants.FEEDBACK, method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
ResponseEntity<Void> postBasedeploymentActionFeedback(@PathVariable("tenant") final String tenant,
|
||||
@Valid final DdiActionFeedback feedback, @PathVariable("targetid") final String targetid,
|
||||
ResponseEntity<Void> postBasedeploymentActionFeedback(@Valid final DdiActionFeedback feedback,
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("actionId") @NotEmpty final Long actionId);
|
||||
|
||||
/**
|
||||
* This is the feedback channel for the config data action.
|
||||
*
|
||||
* @param tenant
|
||||
* of the client
|
||||
* @param configData
|
||||
* as body
|
||||
* @param targetid
|
||||
* @param controllerId
|
||||
* to provide data for
|
||||
* @param request
|
||||
* the HTTP request injected by spring
|
||||
*
|
||||
* @return status of the request
|
||||
*/
|
||||
@RequestMapping(value = "/{targetid}/"
|
||||
@RequestMapping(value = "/{controllerId}/"
|
||||
+ DdiRestConstants.CONFIG_DATA_ACTION, method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
ResponseEntity<Void> putConfigData(@PathVariable("tenant") final String tenant,
|
||||
@Valid final DdiConfigData configData, @PathVariable("targetid") final String targetid);
|
||||
ResponseEntity<Void> putConfigData(@Valid final DdiConfigData configData,
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId);
|
||||
|
||||
/**
|
||||
* RequestMethod.GET method for the {@link DdiCancel} action.
|
||||
*
|
||||
* @param targetid
|
||||
* @param tenant
|
||||
* of the request
|
||||
* @param controllerId
|
||||
* ID of the calling target
|
||||
* @param actionId
|
||||
* of the action
|
||||
@@ -185,19 +201,21 @@ public interface DdiRootControllerRestApi {
|
||||
*
|
||||
* @return the {@link DdiCancel} response
|
||||
*/
|
||||
@RequestMapping(value = "/{targetid}/" + DdiRestConstants.CANCEL_ACTION
|
||||
@RequestMapping(value = "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION
|
||||
+ "/{actionId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
ResponseEntity<DdiCancel> getControllerCancelAction(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("targetid") @NotEmpty final String targetid,
|
||||
@PathVariable("controllerId") @NotEmpty final String controllerId,
|
||||
@PathVariable("actionId") @NotEmpty final Long actionId);
|
||||
|
||||
/**
|
||||
* RequestMethod.POST method receiving the {@link DdiActionFeedback} from
|
||||
* the target.
|
||||
*
|
||||
* @param tenant
|
||||
* of the client
|
||||
* @param feedback
|
||||
* the {@link DdiActionFeedback} from the target.
|
||||
* @param targetid
|
||||
* @param controllerId
|
||||
* the ID of the calling target
|
||||
* @param actionId
|
||||
* of the action we have feedback for
|
||||
@@ -207,10 +225,11 @@ public interface DdiRootControllerRestApi {
|
||||
* @return the {@link DdiActionFeedback} response
|
||||
*/
|
||||
|
||||
@RequestMapping(value = "/{targetid}/" + DdiRestConstants.CANCEL_ACTION + "/{actionId}/"
|
||||
@RequestMapping(value = "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION + "/{actionId}/"
|
||||
+ DdiRestConstants.FEEDBACK, method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
ResponseEntity<Void> postCancelActionFeedback(@PathVariable("tenant") final String tenant,
|
||||
@Valid final DdiActionFeedback feedback, @PathVariable("targetid") @NotEmpty final String targetid,
|
||||
ResponseEntity<Void> postCancelActionFeedback(@Valid final DdiActionFeedback feedback,
|
||||
@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("controllerId") @NotEmpty final String controllerId,
|
||||
@PathVariable("actionId") @NotEmpty final Long actionId);
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.io.InputStream;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.web.bind.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
@@ -27,7 +27,9 @@ public interface DdiDlArtifactStoreControllerRestApi {
|
||||
/**
|
||||
* Handles GET download request. This could be full or partial download
|
||||
* request.
|
||||
*
|
||||
*
|
||||
* @param tenant
|
||||
* name of the client
|
||||
* @param fileName
|
||||
* to search for
|
||||
* @param targetid
|
||||
@@ -40,12 +42,14 @@ public interface DdiDlArtifactStoreControllerRestApi {
|
||||
@RequestMapping(method = RequestMethod.GET, value = DdiDlRestConstants.ARTIFACT_DOWNLOAD_BY_FILENAME
|
||||
+ "/{fileName}")
|
||||
@ResponseBody
|
||||
public ResponseEntity<InputStream> downloadArtifactByFilename(@PathVariable("tenant") final String tenant,
|
||||
ResponseEntity<InputStream> downloadArtifactByFilename(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("fileName") final String fileName, @AuthenticationPrincipal final String targetid);
|
||||
|
||||
/**
|
||||
* Handles GET MD5 checksum file download request.
|
||||
*
|
||||
* @param tenant
|
||||
* name of the client
|
||||
* @param fileName
|
||||
* to search for
|
||||
*
|
||||
@@ -54,7 +58,7 @@ public interface DdiDlArtifactStoreControllerRestApi {
|
||||
@RequestMapping(method = RequestMethod.GET, value = DdiDlRestConstants.ARTIFACT_DOWNLOAD_BY_FILENAME + "/{fileName}"
|
||||
+ DdiDlRestConstants.ARTIFACT_MD5_DWNL_SUFFIX)
|
||||
@ResponseBody
|
||||
public ResponseEntity<Void> downloadArtifactMD5ByFilename(@PathVariable("tenant") final String tenant,
|
||||
ResponseEntity<Void> downloadArtifactMD5ByFilename(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("fileName") final String fileName);
|
||||
|
||||
}
|
||||
|
||||
@@ -12,15 +12,16 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.hawkbit.api.ApiType;
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
|
||||
import org.eclipse.hawkbit.api.UrlProtocol;
|
||||
import org.eclipse.hawkbit.api.URLPlaceholder;
|
||||
import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData;
|
||||
import org.eclipse.hawkbit.ddi.dl.rest.api.DdiDlRestConstants;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiArtifact;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiArtifactHash;
|
||||
@@ -29,6 +30,7 @@ import org.eclipse.hawkbit.ddi.json.model.DdiConfig;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiControllerBase;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiPolling;
|
||||
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -46,11 +48,11 @@ public final class DataConversionHelper {
|
||||
|
||||
}
|
||||
|
||||
static List<DdiChunk> createChunks(final String targetid, final Action uAction,
|
||||
final ArtifactUrlHandler artifactUrlHandler) {
|
||||
static List<DdiChunk> createChunks(final Target target, final Action uAction,
|
||||
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement) {
|
||||
return uAction.getDistributionSet().getModules().stream()
|
||||
.map(module -> new DdiChunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(),
|
||||
module.getName(), createArtifacts(targetid, module, artifactUrlHandler)))
|
||||
module.getName(), createArtifacts(target, module, artifactUrlHandler, systemManagement)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
@@ -69,43 +71,42 @@ public final class DataConversionHelper {
|
||||
/**
|
||||
* Creates all (rest) artifacts for a given software module.
|
||||
*
|
||||
* @param targetid
|
||||
* of the target
|
||||
* @param target
|
||||
* for create URLs for
|
||||
* @param module
|
||||
* the software module
|
||||
* @param artifactUrlHandler
|
||||
* for creating download URLs
|
||||
* @param systemManagement
|
||||
* for access to tenant meta data
|
||||
* @return a list of artifacts or a empty list. Cannot be <null>.
|
||||
*/
|
||||
public static List<DdiArtifact> createArtifacts(final String targetid,
|
||||
public static List<DdiArtifact> createArtifacts(final Target target,
|
||||
final org.eclipse.hawkbit.repository.model.SoftwareModule module,
|
||||
final ArtifactUrlHandler artifactUrlHandler) {
|
||||
final List<DdiArtifact> files = new ArrayList<>();
|
||||
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement) {
|
||||
|
||||
module.getLocalArtifacts()
|
||||
.forEach(artifact -> files.add(createArtifact(targetid, artifactUrlHandler, artifact)));
|
||||
return files;
|
||||
return module.getLocalArtifacts().stream()
|
||||
.map(artifact -> createArtifact(target, artifactUrlHandler, artifact, systemManagement))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static DdiArtifact createArtifact(final String targetid, final ArtifactUrlHandler artifactUrlHandler,
|
||||
final LocalArtifact artifact) {
|
||||
private static DdiArtifact createArtifact(final Target target, final ArtifactUrlHandler artifactUrlHandler,
|
||||
final LocalArtifact artifact, final SystemManagement systemManagement) {
|
||||
final DdiArtifact file = new DdiArtifact();
|
||||
file.setHashes(new DdiArtifactHash(artifact.getSha1Hash(), artifact.getMd5Hash()));
|
||||
file.setFilename(artifact.getFilename());
|
||||
file.setSize(artifact.getSize());
|
||||
|
||||
if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTP)) {
|
||||
final String linkHttp = artifactUrlHandler.getUrl(targetid, artifact.getSoftwareModule().getId(),
|
||||
artifact.getFilename(), artifact.getSha1Hash(), UrlProtocol.HTTP);
|
||||
file.add(new Link(linkHttp).withRel("download-http"));
|
||||
file.add(new Link(linkHttp + DdiDlRestConstants.ARTIFACT_MD5_DWNL_SUFFIX).withRel("md5sum-http"));
|
||||
}
|
||||
artifactUrlHandler
|
||||
.getUrls(new URLPlaceholder(systemManagement.getTenantMetadata().getTenant(),
|
||||
systemManagement.getTenantMetadata().getId(), target.getControllerId(), target.getId(),
|
||||
new SoftwareData(artifact.getSoftwareModule().getId(), artifact.getFilename(), artifact.getId(),
|
||||
artifact.getSha1Hash())),
|
||||
ApiType.DDI)
|
||||
.forEach(entry -> file.add(new Link(entry.getRef()).withRel(entry.getRel())));
|
||||
|
||||
if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTPS)) {
|
||||
final String linkHttps = artifactUrlHandler.getUrl(targetid, artifact.getSoftwareModule().getId(),
|
||||
artifact.getFilename(), artifact.getSha1Hash(), UrlProtocol.HTTPS);
|
||||
file.add(new Link(linkHttps).withRel("download"));
|
||||
file.add(new Link(linkHttps + DdiDlRestConstants.ARTIFACT_MD5_DWNL_SUFFIX).withRel("md5sum"));
|
||||
}
|
||||
return file;
|
||||
|
||||
}
|
||||
|
||||
static DdiControllerBase fromTarget(final Target target, final Optional<Action> action,
|
||||
@@ -133,8 +134,8 @@ public final class DataConversionHelper {
|
||||
}
|
||||
|
||||
if (target.getTargetInfo().isRequestControllerAttributes()) {
|
||||
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
||||
.putConfigData(tenantAware.getCurrentTenant(), null, target.getControllerId()))
|
||||
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant()).putConfigData(null,
|
||||
tenantAware.getCurrentTenant(), target.getControllerId()))
|
||||
.withRel(DdiRestConstants.CONFIG_DATA_ACTION));
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.web.bind.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.SoftwareManagement;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
@@ -88,6 +89,9 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
|
||||
@Autowired
|
||||
private SystemManagement systemManagement;
|
||||
|
||||
@Autowired
|
||||
private ArtifactUrlHandler artifactUrlHandler;
|
||||
|
||||
@@ -99,9 +103,12 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
|
||||
@Override
|
||||
public ResponseEntity<List<org.eclipse.hawkbit.ddi.json.model.DdiArtifact>> getSoftwareModulesArtifacts(
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("targetid") final String targetid,
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("softwareModuleId") final Long softwareModuleId) {
|
||||
LOG.debug("getSoftwareModulesArtifacts({})", targetid);
|
||||
LOG.debug("getSoftwareModulesArtifacts({})", controllerId);
|
||||
|
||||
final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil
|
||||
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
|
||||
final SoftwareModule softwareModule = softwareManagement.findSoftwareModuleById(softwareModuleId);
|
||||
|
||||
@@ -111,20 +118,21 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
|
||||
}
|
||||
|
||||
return new ResponseEntity<>(DataConversionHelper.createArtifacts(targetid, softwareModule, artifactUrlHandler),
|
||||
return new ResponseEntity<>(
|
||||
DataConversionHelper.createArtifacts(target, softwareModule, artifactUrlHandler, systemManagement),
|
||||
HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<DdiControllerBase> getControllerBase(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("targetid") final String targetid) {
|
||||
LOG.debug("getControllerBase({})", targetid);
|
||||
@PathVariable("controllerId") final String controllerId) {
|
||||
LOG.debug("getControllerBase({})", controllerId);
|
||||
|
||||
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(targetid, IpUtil
|
||||
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(controllerId, IpUtil
|
||||
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
|
||||
if (target.getTargetInfo().getUpdateStatus() == TargetUpdateStatus.UNKNOWN) {
|
||||
LOG.debug("target with {} extsisted but was in status UNKNOWN -> REGISTERED)", targetid);
|
||||
LOG.debug("target with {} extsisted but was in status UNKNOWN -> REGISTERED)", controllerId);
|
||||
controllerManagement.updateTargetStatus(target.getTargetInfo(), TargetUpdateStatus.REGISTERED,
|
||||
System.currentTimeMillis(), IpUtil.getClientIpFromRequest(
|
||||
requestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
@@ -138,12 +146,12 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
|
||||
@Override
|
||||
public ResponseEntity<InputStream> downloadArtifact(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("targetid") final String targetid,
|
||||
@PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||
@PathVariable("fileName") final String fileName) {
|
||||
ResponseEntity<InputStream> result;
|
||||
|
||||
final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil
|
||||
final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil
|
||||
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId);
|
||||
|
||||
@@ -151,7 +159,12 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
LOG.warn("Softare module with id {} could not be found.", softwareModuleId);
|
||||
result = new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
} else {
|
||||
|
||||
// Exception squid:S3655 - Optional access is checked in checkModule
|
||||
// subroutine
|
||||
@SuppressWarnings("squid:S3655")
|
||||
final LocalArtifact artifact = module.getLocalArtifactByFilename(fileName).get();
|
||||
|
||||
final DbArtifact file = artifactManagement.loadLocalArtifactBinary(artifact);
|
||||
|
||||
final String ifMatch = requestResponseContextHolder.getHttpServletRequest().getHeader("If-Match");
|
||||
@@ -196,11 +209,14 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
// Exception squid:S3655 - Optional access is checked in checkModule
|
||||
// subroutine
|
||||
@SuppressWarnings("squid:S3655")
|
||||
public ResponseEntity<Void> downloadArtifactMd5(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("targetid") final String targetid,
|
||||
@PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||
@PathVariable("fileName") final String fileName) {
|
||||
controllerManagement.updateLastTargetQuery(targetid, IpUtil
|
||||
controllerManagement.updateLastTargetQuery(controllerId, IpUtil
|
||||
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
|
||||
final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId);
|
||||
@@ -223,12 +239,12 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
|
||||
@Override
|
||||
public ResponseEntity<DdiDeploymentBase> getControllerBasedeploymentAction(
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("targetid") final String targetid,
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("actionId") final Long actionId,
|
||||
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource) {
|
||||
LOG.debug("getControllerBasedeploymentAction({},{})", targetid, resource);
|
||||
LOG.debug("getControllerBasedeploymentAction({},{})", controllerId, resource);
|
||||
|
||||
final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil
|
||||
final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil
|
||||
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
|
||||
final Action action = findActionWithExceptionIfNotFound(actionId);
|
||||
@@ -239,14 +255,15 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
|
||||
if (!action.isCancelingOrCanceled()) {
|
||||
|
||||
final List<DdiChunk> chunks = DataConversionHelper.createChunks(targetid, action, artifactUrlHandler);
|
||||
final List<DdiChunk> chunks = DataConversionHelper.createChunks(target, action, artifactUrlHandler,
|
||||
systemManagement);
|
||||
|
||||
final HandlingType handlingType = action.isForce() ? HandlingType.FORCED : HandlingType.ATTEMPT;
|
||||
|
||||
final DdiDeploymentBase base = new DdiDeploymentBase(Long.toString(action.getId()),
|
||||
new DdiDeployment(handlingType, handlingType, chunks));
|
||||
|
||||
LOG.debug("Found an active UpdateAction for target {}. returning deyploment: {}", targetid, base);
|
||||
LOG.debug("Found an active UpdateAction for target {}. returning deyploment: {}", controllerId, base);
|
||||
|
||||
controllerManagement.registerRetrieved(action, RepositoryConstants.SERVER_MESSAGE_PREFIX
|
||||
+ "Target retrieved update action and should start now the download.");
|
||||
@@ -258,12 +275,12 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> postBasedeploymentActionFeedback(@PathVariable("tenant") final String tenant,
|
||||
@Valid @RequestBody final DdiActionFeedback feedback, @PathVariable("targetid") final String targetid,
|
||||
public ResponseEntity<Void> postBasedeploymentActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback,
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("actionId") @NotEmpty final Long actionId) {
|
||||
LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", targetid, actionId, feedback);
|
||||
LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", controllerId, actionId, feedback);
|
||||
|
||||
final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil
|
||||
final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil
|
||||
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
|
||||
if (!actionId.equals(feedback.getId())) {
|
||||
@@ -285,13 +302,14 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
return new ResponseEntity<>(HttpStatus.GONE);
|
||||
}
|
||||
|
||||
controllerManagement.addUpdateActionStatus(generateUpdateStatus(feedback, targetid, feedback.getId(), action));
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(generateUpdateStatus(feedback, controllerId, feedback.getId(), action));
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
|
||||
}
|
||||
|
||||
private ActionStatus generateUpdateStatus(final DdiActionFeedback feedback, final String targetid,
|
||||
private ActionStatus generateUpdateStatus(final DdiActionFeedback feedback, final String controllerId,
|
||||
final Long actionid, final Action action) {
|
||||
|
||||
final ActionStatus actionStatus = entityFactory.generateActionStatus();
|
||||
@@ -300,22 +318,22 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
|
||||
switch (feedback.getStatus().getExecution()) {
|
||||
case CANCELED:
|
||||
LOG.debug("Controller confirmed cancel (actionid: {}, targetid: {}) as we got {} report.", actionid,
|
||||
targetid, feedback.getStatus().getExecution());
|
||||
LOG.debug("Controller confirmed cancel (actionid: {}, controllerId: {}) as we got {} report.", actionid,
|
||||
controllerId, feedback.getStatus().getExecution());
|
||||
actionStatus.setStatus(Status.CANCELED);
|
||||
actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target confirmed cancelation.");
|
||||
break;
|
||||
case REJECTED:
|
||||
LOG.info("Controller reported internal error (actionid: {}, targetid: {}) as we got {} report.", actionid,
|
||||
targetid, feedback.getStatus().getExecution());
|
||||
LOG.info("Controller reported internal error (actionid: {}, controllerId: {}) as we got {} report.",
|
||||
actionid, controllerId, feedback.getStatus().getExecution());
|
||||
actionStatus.setStatus(Status.WARNING);
|
||||
actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target REJECTED update.");
|
||||
break;
|
||||
case CLOSED:
|
||||
handleClosedUpdateStatus(feedback, targetid, actionid, actionStatus);
|
||||
handleClosedUpdateStatus(feedback, controllerId, actionid, actionStatus);
|
||||
break;
|
||||
default:
|
||||
handleDefaultUpdateStatus(feedback, targetid, actionid, actionStatus);
|
||||
handleDefaultUpdateStatus(feedback, controllerId, actionid, actionStatus);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -331,19 +349,19 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
return actionStatus;
|
||||
}
|
||||
|
||||
private static void handleDefaultUpdateStatus(final DdiActionFeedback feedback, final String targetid,
|
||||
private static void handleDefaultUpdateStatus(final DdiActionFeedback feedback, final String controllerId,
|
||||
final Long actionid, final ActionStatus actionStatus) {
|
||||
LOG.debug("Controller reported intermediate status (actionid: {}, targetid: {}) as we got {} report.", actionid,
|
||||
targetid, feedback.getStatus().getExecution());
|
||||
LOG.debug("Controller reported intermediate status (actionid: {}, controllerId: {}) as we got {} report.",
|
||||
actionid, controllerId, feedback.getStatus().getExecution());
|
||||
actionStatus.setStatus(Status.RUNNING);
|
||||
actionStatus.addMessage(
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target reported " + feedback.getStatus().getExecution());
|
||||
}
|
||||
|
||||
private static void handleClosedUpdateStatus(final DdiActionFeedback feedback, final String targetid,
|
||||
private static void handleClosedUpdateStatus(final DdiActionFeedback feedback, final String controllerId,
|
||||
final Long actionid, final ActionStatus actionStatus) {
|
||||
LOG.debug("Controller reported closed (actionid: {}, targetid: {}) as we got {} report.", actionid, targetid,
|
||||
feedback.getStatus().getExecution());
|
||||
LOG.debug("Controller reported closed (actionid: {}, controllerId: {}) as we got {} report.", actionid,
|
||||
controllerId, feedback.getStatus().getExecution());
|
||||
if (feedback.getStatus().getResult().getFinished() == FinalResult.FAILURE) {
|
||||
actionStatus.setStatus(Status.ERROR);
|
||||
actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target reported CLOSED with ERROR!");
|
||||
@@ -354,23 +372,23 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> putConfigData(@PathVariable("tenant") final String tenant,
|
||||
@Valid @RequestBody final DdiConfigData configData, @PathVariable("targetid") final String targetid) {
|
||||
controllerManagement.updateLastTargetQuery(targetid, IpUtil
|
||||
public ResponseEntity<Void> putConfigData(@Valid @RequestBody final DdiConfigData configData,
|
||||
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId) {
|
||||
controllerManagement.updateLastTargetQuery(controllerId, IpUtil
|
||||
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
|
||||
controllerManagement.updateControllerAttributes(targetid, configData.getData());
|
||||
controllerManagement.updateControllerAttributes(controllerId, configData.getData());
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<DdiCancel> getControllerCancelAction(@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("targetid") @NotEmpty final String targetid,
|
||||
@PathVariable("controllerId") @NotEmpty final String controllerId,
|
||||
@PathVariable("actionId") @NotEmpty final Long actionId) {
|
||||
LOG.debug("getControllerCancelAction({})", targetid);
|
||||
LOG.debug("getControllerCancelAction({})", controllerId);
|
||||
|
||||
final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil
|
||||
final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil
|
||||
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
|
||||
final Action action = findActionWithExceptionIfNotFound(actionId);
|
||||
@@ -383,7 +401,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
final DdiCancel cancel = new DdiCancel(String.valueOf(action.getId()),
|
||||
new DdiCancelActionToStop(String.valueOf(action.getId())));
|
||||
|
||||
LOG.debug("Found an active CancelAction for target {}. returning cancel: {}", targetid, cancel);
|
||||
LOG.debug("Found an active CancelAction for target {}. returning cancel: {}", controllerId, cancel);
|
||||
|
||||
controllerManagement.registerRetrieved(action, RepositoryConstants.SERVER_MESSAGE_PREFIX
|
||||
+ "Target retrieved cancel action and should start now the cancelation.");
|
||||
@@ -395,13 +413,13 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> postCancelActionFeedback(@PathVariable("tenant") final String tenant,
|
||||
@Valid @RequestBody final DdiActionFeedback feedback,
|
||||
@PathVariable("targetid") @NotEmpty final String targetid,
|
||||
public ResponseEntity<Void> postCancelActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback,
|
||||
@PathVariable("tenant") final String tenant,
|
||||
@PathVariable("controllerId") @NotEmpty final String controllerId,
|
||||
@PathVariable("actionId") @NotEmpty final Long actionId) {
|
||||
LOG.debug("provideCancelActionFeedback for target [{}]: {}", targetid, feedback);
|
||||
LOG.debug("provideCancelActionFeedback for target [{}]: {}", controllerId, feedback);
|
||||
|
||||
final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil
|
||||
final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil
|
||||
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
|
||||
if (!actionId.equals(feedback.getId())) {
|
||||
@@ -432,13 +450,13 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
switch (feedback.getStatus().getExecution()) {
|
||||
case CANCELED:
|
||||
LOG.error(
|
||||
"Controller reported cancel for a cancel which is not supported by the server (actionid: {}, targetid: {}) as we got {} report.",
|
||||
"Controller reported cancel for a cancel which is not supported by the server (actionid: {}, controllerId: {}) as we got {} report.",
|
||||
actionid, target.getControllerId(), feedback.getStatus().getExecution());
|
||||
actionStatus.setStatus(Status.WARNING);
|
||||
break;
|
||||
case REJECTED:
|
||||
LOG.info("Controller rejected the cancelation request (too late) (actionid: {}, targetid: {}).", actionid,
|
||||
target.getControllerId());
|
||||
LOG.info("Controller rejected the cancelation request (too late) (actionid: {}, controllerId: {}).",
|
||||
actionid, target.getControllerId());
|
||||
actionStatus.setStatus(Status.WARNING);
|
||||
break;
|
||||
case CLOSED:
|
||||
|
||||
@@ -61,8 +61,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
|
||||
@Stories("Deployment Action Resource")
|
||||
public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoDB {
|
||||
|
||||
private static final String HTTP_LOCALHOST = "http://localhost/";
|
||||
private static final String HTTPS_LOCALHOST = "https://localhost/";
|
||||
private static final String HTTP_LOCALHOST = "http://localhost:8080/";
|
||||
|
||||
@Test()
|
||||
@Description("Ensures that artifacts are not found, when softare module does not exists.")
|
||||
@@ -174,25 +173,14 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1",
|
||||
contains(artifact.getSha1Hash())))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href",
|
||||
contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1")))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href",
|
||||
contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.MD5SUM")))
|
||||
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download-http.href",
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1")))
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum-http.href",
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
@@ -203,27 +191,17 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
|
||||
contains("test1.signature")))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.md5",
|
||||
contains(artifactSignature.getMd5Hash())))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1",
|
||||
contains(artifactSignature.getSha1Hash())))
|
||||
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1",
|
||||
contains(artifactSignature.getSha1Hash())))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href",
|
||||
contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.signature")))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href",
|
||||
contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.signature.MD5SUM")))
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href",
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.signature")))
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href",
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
@@ -359,16 +337,18 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1",
|
||||
contains(artifact.getSha1Hash())))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href",
|
||||
contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1")))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href",
|
||||
contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.MD5SUM")))
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1")))
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.MD5SUM")))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].size", contains(5 * 1024)))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].filename",
|
||||
contains("test1.signature")))
|
||||
@@ -377,24 +357,14 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1",
|
||||
contains(artifactSignature.getSha1Hash())))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href",
|
||||
contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.signature")))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href",
|
||||
contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.signature.MD5SUM")))
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href",
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.signature")))
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href",
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
@@ -486,31 +456,22 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].filename", contains("test1")))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.md5",
|
||||
contains(artifact.getMd5Hash())))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1",
|
||||
contains(artifact.getSha1Hash())))
|
||||
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href",
|
||||
contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1")))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href",
|
||||
contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.MD5SUM")))
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download-http.href",
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1",
|
||||
contains(artifact.getSha1Hash())))
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1")))
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum-http.href",
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.MD5SUM")))
|
||||
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].size", contains(5 * 1024)))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].filename",
|
||||
contains("test1.signature")))
|
||||
@@ -519,25 +480,14 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1",
|
||||
contains(artifactSignature.getSha1Hash())))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href",
|
||||
contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.signature")))
|
||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href",
|
||||
contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.signature.MD5SUM")))
|
||||
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href",
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
+ "/artifacts/test1.signature")))
|
||||
.andExpect(
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href",
|
||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href",
|
||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/softwaremodules/"
|
||||
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
spring.data.mongodb.uri=mongodb://localhost/spArtifactRepository${random.value}
|
||||
spring.data.mongodb.port=28017
|
||||
|
||||
hawkbit.server.ddi.security.authentication.header.enabled=true
|
||||
hawkbit.server.ddi.security.authentication.gatewaytoken.name=TestToken
|
||||
|
||||
multipart.max-file-size=5MB
|
||||
|
||||
hawkbit.server.security.dos.maxStatusEntriesPerAction=100
|
||||
|
||||
hawkbit.server.security.dos.maxAttributeEntriesPerTarget=10
|
||||
|
||||
spring.jpa.database=H2
|
||||
spring.datasource.url=jdbc:h2:mem:sp-db;DB_CLOSE_ON_EXIT=FALSE
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=sa
|
||||
|
||||
flyway.enabled=true
|
||||
flyway.sqlMigrationSuffix=${spring.jpa.database}.sql
|
||||
#spring.jpa.show-sql=true
|
||||
|
||||
# DDI configuration
|
||||
hawkbit.controller.pollingTime=00:01:00
|
||||
hawkbit.controller.pollingOverdueTime=00:01:00
|
||||
|
||||
hawkbit.artifact.url.protocols[0].rel=download
|
||||
hawkbit.artifact.url.protocols[0].protocol=http
|
||||
hawkbit.artifact.url.protocols[0].supports=DMF,DDI
|
||||
hawkbit.artifact.url.protocols[0].ref={protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}
|
||||
hawkbit.artifact.url.protocols[1].rel=md5sum
|
||||
hawkbit.artifact.url.protocols[1].protocol=${hawkbit.artifact.url.protocols[0].protocol}
|
||||
hawkbit.artifact.url.protocols[1].supports=${hawkbit.artifact.url.protocols[0].supports}
|
||||
hawkbit.artifact.url.protocols[1].ref=${hawkbit.artifact.url.protocols[0].ref}.MD5SUM
|
||||
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
* 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.amqp;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.eclipse.hawkbit.api.HostnameResolver;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
||||
import org.eclipse.hawkbit.cache.DownloadArtifactCache;
|
||||
import org.eclipse.hawkbit.cache.DownloadType;
|
||||
import org.eclipse.hawkbit.dmf.json.model.Artifact;
|
||||
import org.eclipse.hawkbit.dmf.json.model.ArtifactHash;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
|
||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.CredentialsExpiredException;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
* {@link AmqpMessageHandlerService} handles all incoming target authentication
|
||||
* AMQP messages that can be used by 3rd party CDN services to check if a target
|
||||
* is permitted to download certain artifact. This is handled by the queue that
|
||||
* is configured for the property
|
||||
* hawkbit.dmf.rabbitmq.authenticationReceiverQueue.
|
||||
*
|
||||
*/
|
||||
public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AmqpAuthenticationMessageHandler.class);
|
||||
|
||||
private final AmqpControllerAuthentication authenticationManager;
|
||||
|
||||
private final ArtifactManagement artifactManagement;
|
||||
|
||||
private final Cache cache;
|
||||
|
||||
private final HostnameResolver hostnameResolver;
|
||||
|
||||
private final ControllerManagement controllerManagement;
|
||||
|
||||
/**
|
||||
* @param rabbitTemplate
|
||||
* the configured amqp template.
|
||||
* @param artifactManagement
|
||||
* for artifact URI generation
|
||||
* @param cache
|
||||
* for download Ids
|
||||
* @param hostnameResolver
|
||||
* for resolving the host for downloads
|
||||
* @param authenticationManager
|
||||
* for target authentication
|
||||
* @param controllerManagement
|
||||
* for target repo access
|
||||
*/
|
||||
public AmqpAuthenticationMessageHandler(final RabbitTemplate rabbitTemplate,
|
||||
final AmqpControllerAuthentication authenticationManager, final ArtifactManagement artifactManagement,
|
||||
final Cache cache, final HostnameResolver hostnameResolver,
|
||||
final ControllerManagement controllerManagement) {
|
||||
super(rabbitTemplate);
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.artifactManagement = artifactManagement;
|
||||
this.cache = cache;
|
||||
this.hostnameResolver = hostnameResolver;
|
||||
this.controllerManagement = controllerManagement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed on a authentication request.
|
||||
*
|
||||
* @param message
|
||||
* the amqp message
|
||||
* @return the rpc message back to supplier.
|
||||
*/
|
||||
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue}", containerFactory = "listenerContainerFactory")
|
||||
public Message onAuthenticationRequest(final Message message) {
|
||||
checkContentTypeJson(message);
|
||||
final SecurityContext oldContext = SecurityContextHolder.getContext();
|
||||
try {
|
||||
return handleAuthenticationMessage(message);
|
||||
} catch (final RuntimeException ex) {
|
||||
throw new AmqpRejectAndDontRequeueException(ex);
|
||||
} finally {
|
||||
SecurityContextHolder.setContext(oldContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check action for this download purposes, the method will throw an
|
||||
* EntityNotFoundException in case the controller is not allowed to download
|
||||
* this file because it's not assigned to an action and not assigned to this
|
||||
* controller. Otherwise no controllerId is set = anonymous download
|
||||
*
|
||||
* @param secruityToken
|
||||
* the security token which holds the target ID to check on
|
||||
* @param localArtifact
|
||||
* the local artifact to verify if the given target is allowed to
|
||||
* download this artifact
|
||||
*/
|
||||
private void checkIfArtifactIsAssignedToTarget(final TenantSecurityToken secruityToken,
|
||||
final LocalArtifact localArtifact) {
|
||||
|
||||
if (secruityToken.getControllerId() != null) {
|
||||
checkByControllerId(localArtifact, secruityToken.getControllerId());
|
||||
} else if (secruityToken.getTargetId() != null) {
|
||||
checkByTargetId(localArtifact, secruityToken.getTargetId());
|
||||
} else {
|
||||
LOG.info("anonymous download no authentication check for artifact {}", localArtifact);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void checkByTargetId(final LocalArtifact localArtifact, final Long targetId) {
|
||||
LOG.debug("no anonymous download request, doing authentication check for target {} and artifact {}", targetId,
|
||||
localArtifact);
|
||||
if (!controllerManagement.hasTargetArtifactAssigned(targetId, localArtifact)) {
|
||||
LOG.info("target {} tried to download artifact {} which is not assigned to the target", targetId,
|
||||
localArtifact);
|
||||
throw new EntityNotFoundException();
|
||||
}
|
||||
LOG.info("download security check for target {} and artifact {} granted", targetId, localArtifact);
|
||||
}
|
||||
|
||||
private void checkByControllerId(final LocalArtifact localArtifact, final String controllerId) {
|
||||
LOG.debug("no anonymous download request, doing authentication check for target {} and artifact {}",
|
||||
controllerId, localArtifact);
|
||||
if (!controllerManagement.hasTargetArtifactAssigned(controllerId, localArtifact)) {
|
||||
LOG.info("target {} tried to download artifact {} which is not assigned to the target", controllerId,
|
||||
localArtifact);
|
||||
throw new EntityNotFoundException();
|
||||
}
|
||||
LOG.info("download security check for target {} and artifact {} granted", controllerId, localArtifact);
|
||||
}
|
||||
|
||||
private LocalArtifact findLocalArtifactByFileResource(final FileResource fileResource) {
|
||||
if (fileResource.getSha1() != null) {
|
||||
return artifactManagement.findFirstLocalArtifactsBySHA1(fileResource.getSha1());
|
||||
} else if (fileResource.getFilename() != null) {
|
||||
return artifactManagement.findLocalArtifactByFilename(fileResource.getFilename()).stream().findFirst()
|
||||
.orElse(null);
|
||||
} else if (fileResource.getArtifactId() != null) {
|
||||
return artifactManagement.findLocalArtifact(fileResource.getArtifactId());
|
||||
} else if (fileResource.getSoftwareModuleFilenameResource() != null) {
|
||||
return artifactManagement
|
||||
.findByFilenameAndSoftwareModule(fileResource.getSoftwareModuleFilenameResource().getFilename(),
|
||||
fileResource.getSoftwareModuleFilenameResource().getSoftwareModuleId())
|
||||
.stream().findFirst().orElse(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Artifact convertDbArtifact(final DbArtifact dbArtifact) {
|
||||
final Artifact artifact = new Artifact();
|
||||
artifact.setSize(dbArtifact.getSize());
|
||||
final DbArtifactHash dbArtifactHash = dbArtifact.getHashes();
|
||||
artifact.setHashes(new ArtifactHash(dbArtifactHash.getSha1(), dbArtifactHash.getMd5()));
|
||||
return artifact;
|
||||
}
|
||||
|
||||
private Message handleAuthenticationMessage(final Message message) {
|
||||
final DownloadResponse authentificationResponse = new DownloadResponse();
|
||||
final MessageProperties messageProperties = message.getMessageProperties();
|
||||
final TenantSecurityToken secruityToken = convertMessage(message, TenantSecurityToken.class);
|
||||
|
||||
final FileResource fileResource = secruityToken.getFileResource();
|
||||
try {
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken));
|
||||
|
||||
final LocalArtifact localArtifact = findLocalArtifactByFileResource(fileResource);
|
||||
|
||||
if (localArtifact == null) {
|
||||
LOG.info("target {} requested file resource {} which does not exists to download",
|
||||
secruityToken.getControllerId(), fileResource);
|
||||
throw new EntityNotFoundException();
|
||||
}
|
||||
|
||||
checkIfArtifactIsAssignedToTarget(secruityToken, localArtifact);
|
||||
|
||||
final Artifact artifact = convertDbArtifact(artifactManagement.loadLocalArtifactBinary(localArtifact));
|
||||
if (artifact == null) {
|
||||
throw new EntityNotFoundException();
|
||||
}
|
||||
authentificationResponse.setArtifact(artifact);
|
||||
final String downloadId = UUID.randomUUID().toString();
|
||||
// SHA1 key is set, download by SHA1
|
||||
final DownloadArtifactCache downloadCache = new DownloadArtifactCache(DownloadType.BY_SHA1,
|
||||
localArtifact.getSha1Hash());
|
||||
cache.put(downloadId, downloadCache);
|
||||
authentificationResponse
|
||||
.setDownloadUrl(UriComponentsBuilder.fromUri(hostnameResolver.resolveHostname().toURI())
|
||||
.path("/api/v1/downloadserver/downloadId/").path(downloadId).build().toUriString());
|
||||
authentificationResponse.setResponseCode(HttpStatus.OK.value());
|
||||
} catch (final BadCredentialsException | AuthenticationServiceException | CredentialsExpiredException e) {
|
||||
LOG.error("Login failed", e);
|
||||
authentificationResponse.setResponseCode(HttpStatus.FORBIDDEN.value());
|
||||
authentificationResponse.setMessage("Login failed");
|
||||
} catch (final URISyntaxException e) {
|
||||
LOG.error("URI build exception", e);
|
||||
authentificationResponse.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
authentificationResponse.setMessage("Building download URI failed");
|
||||
} catch (final EntityNotFoundException e) {
|
||||
final String errorMessage = "Artifact for resource " + fileResource + "not found ";
|
||||
LOG.warn(errorMessage, e);
|
||||
authentificationResponse.setResponseCode(HttpStatus.NOT_FOUND.value());
|
||||
authentificationResponse.setMessage(errorMessage);
|
||||
}
|
||||
|
||||
return getMessageConverter().toMessage(authentificationResponse, messageProperties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,14 +9,18 @@
|
||||
package org.eclipse.hawkbit.amqp;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
|
||||
import org.eclipse.hawkbit.api.HostnameResolver;
|
||||
import org.eclipse.hawkbit.cache.CacheConstants;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
@@ -41,14 +45,17 @@ import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cache.Cache;
|
||||
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 org.springframework.util.ErrorHandler;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Spring configuration for AMQP 0.9 based DMF communication for indirect device
|
||||
* Spring configuration for AMQP based DMF communication for indirect device
|
||||
* integration.
|
||||
*
|
||||
*/
|
||||
@@ -251,17 +258,51 @@ public class AmqpConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create amqp handler service bean.
|
||||
* Create AMQP handler service bean.
|
||||
*
|
||||
* @param rabbitTemplate
|
||||
* for converting messages
|
||||
* @param amqpMessageDispatcherService
|
||||
* to sending events to DMF client
|
||||
* @param controllerManagement
|
||||
* for target repo access
|
||||
* @param entityFactory
|
||||
* to create entities
|
||||
*
|
||||
* @return handler service bean
|
||||
*/
|
||||
@Bean
|
||||
public AmqpMessageHandlerService amqpMessageHandlerService(
|
||||
final AmqpMessageDispatcherService amqpMessageDispatcherService) {
|
||||
return new AmqpMessageHandlerService(rabbitTemplate(), amqpMessageDispatcherService);
|
||||
public AmqpMessageHandlerService amqpMessageHandlerService(final RabbitTemplate rabbitTemplate,
|
||||
final AmqpMessageDispatcherService amqpMessageDispatcherService,
|
||||
final ControllerManagement controllerManagement, final EntityFactory entityFactory) {
|
||||
return new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherService, controllerManagement,
|
||||
entityFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create AMQP handler service bean for authentication messages.
|
||||
*
|
||||
* @param rabbitTemplate
|
||||
* for converting messages
|
||||
* @param authenticationManager
|
||||
* for target authentication
|
||||
* @param artifactManagement
|
||||
* for artifact URI generation
|
||||
* @param cache
|
||||
* for download IDs
|
||||
* @param hostnameResolver
|
||||
* for resolving the host for downloads
|
||||
* @param controllerManagement
|
||||
* for target repo access
|
||||
* @return handler service bean
|
||||
*/
|
||||
@Bean
|
||||
public AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandler(final RabbitTemplate rabbitTemplate,
|
||||
final AmqpControllerAuthentication authenticationManager, final ArtifactManagement artifactManagement,
|
||||
@Qualifier(CacheConstants.DOWNLOAD_ID_CACHE) final Cache cache, final HostnameResolver hostnameResolver,
|
||||
final ControllerManagement controllerManagement) {
|
||||
return new AmqpAuthenticationMessageHandler(rabbitTemplate, authenticationManager, artifactManagement, cache,
|
||||
hostnameResolver, controllerManagement);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,22 +332,25 @@ public class AmqpConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AmqpControllerAuthentication.class)
|
||||
public AmqpControllerAuthentication amqpControllerAuthentication(final ControllerManagement controllerManagement,
|
||||
public AmqpControllerAuthentication amqpControllerAuthentication(final SystemManagement systemManagement,
|
||||
final ControllerManagement controllerManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
|
||||
final DdiSecurityProperties ddiSecruityProperties, final SystemSecurityContext systemSecurityContext) {
|
||||
return new AmqpControllerAuthentication(controllerManagement, tenantConfigurationManagement, tenantAware,
|
||||
ddiSecruityProperties, systemSecurityContext);
|
||||
return new AmqpControllerAuthentication(systemManagement, controllerManagement, tenantConfigurationManagement,
|
||||
tenantAware, ddiSecruityProperties, systemSecurityContext);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AmqpMessageDispatcherService.class)
|
||||
public AmqpMessageDispatcherService amqpMessageDispatcherService(final RabbitTemplate rabbitTemplate,
|
||||
final AmqpSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler) {
|
||||
return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler);
|
||||
final AmqpSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
|
||||
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement) {
|
||||
return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler,
|
||||
systemSecurityContext, systemManagement);
|
||||
}
|
||||
|
||||
private static Map<String, Object> getTTLMaxArgsAuthenticationQueue() {
|
||||
final Map<String, Object> args = new HashMap<>();
|
||||
final Map<String, Object> args = Maps.newHashMapWithExpectedSize(2);
|
||||
args.put("x-message-ttl", Duration.ofSeconds(30).toMillis());
|
||||
args.put("x-max-length", 1_000);
|
||||
return args;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.amqp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@@ -16,6 +15,7 @@ import javax.annotation.PostConstruct;
|
||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter;
|
||||
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousDownload;
|
||||
@@ -32,6 +32,8 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
*
|
||||
* A controller which handles the DMF AMQP authentication.
|
||||
@@ -42,10 +44,12 @@ public class AmqpControllerAuthentication {
|
||||
|
||||
private final PreAuthTokenSourceTrustAuthenticationProvider preAuthenticatedAuthenticationProvider = new PreAuthTokenSourceTrustAuthenticationProvider();
|
||||
|
||||
private final List<PreAuthentificationFilter> filterChain = new ArrayList<>();
|
||||
private List<PreAuthentificationFilter> filterChain;
|
||||
|
||||
private final ControllerManagement controllerManagement;
|
||||
|
||||
private final SystemManagement systemManagement;
|
||||
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
|
||||
private final TenantAware tenantAware;
|
||||
@@ -57,6 +61,7 @@ public class AmqpControllerAuthentication {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param systemManagement
|
||||
* @param controllerManagement
|
||||
* @param tenantConfigurationManagement
|
||||
* @param tenantAware
|
||||
@@ -66,10 +71,12 @@ public class AmqpControllerAuthentication {
|
||||
* @param systemSecurityContext
|
||||
* security context
|
||||
*/
|
||||
public AmqpControllerAuthentication(final ControllerManagement controllerManagement,
|
||||
public AmqpControllerAuthentication(final SystemManagement systemManagement,
|
||||
final ControllerManagement controllerManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
|
||||
final DdiSecurityProperties ddiSecruityProperties, final SystemSecurityContext systemSecurityContext) {
|
||||
this.controllerManagement = controllerManagement;
|
||||
this.systemManagement = systemManagement;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.tenantAware = tenantAware;
|
||||
this.ddiSecruityProperties = ddiSecruityProperties;
|
||||
@@ -85,6 +92,8 @@ public class AmqpControllerAuthentication {
|
||||
}
|
||||
|
||||
private void addFilter() {
|
||||
filterChain = Lists.newArrayListWithExpectedSize(5);
|
||||
|
||||
final ControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new ControllerPreAuthenticatedGatewaySecurityTokenFilter(
|
||||
tenantConfigurationManagement, tenantAware, systemSecurityContext);
|
||||
filterChain.add(gatewaySecurityTokenFilter);
|
||||
@@ -106,13 +115,15 @@ public class AmqpControllerAuthentication {
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs authentication with the secruity token.
|
||||
* Performs authentication with the security token.
|
||||
*
|
||||
* @param secruityToken
|
||||
* the authentication request object
|
||||
* @return the authentfication object
|
||||
* @return the authentication object
|
||||
*/
|
||||
public Authentication doAuthenticate(final TenantSecurityToken secruityToken) {
|
||||
resolveTenant(secruityToken);
|
||||
|
||||
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(null, null);
|
||||
for (final PreAuthentificationFilter filter : filterChain) {
|
||||
final PreAuthenticatedAuthenticationToken authenticationRest = createAuthentication(filter, secruityToken);
|
||||
@@ -126,6 +137,14 @@ public class AmqpControllerAuthentication {
|
||||
|
||||
}
|
||||
|
||||
private void resolveTenant(final TenantSecurityToken securityToken) {
|
||||
if (securityToken.getTenant() == null) {
|
||||
securityToken.setTenant(systemSecurityContext
|
||||
.runAsSystem(() -> systemManagement.getTenantMetadata(securityToken.getTenantId()).getTenant()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static PreAuthenticatedAuthenticationToken createAuthentication(final PreAuthentificationFilter filter,
|
||||
final TenantSecurityToken secruityToken) {
|
||||
|
||||
|
||||
@@ -9,24 +9,26 @@
|
||||
package org.eclipse.hawkbit.amqp;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Bean which holds the necessary properties for configuring the AMQP deadletter
|
||||
* queue.
|
||||
*/
|
||||
@ConfigurationProperties("hawkbit.dmf.rabbitmq.deadLetter")
|
||||
public class AmqpDeadletterProperties {
|
||||
private static final int THREE_WEEKS = 21;
|
||||
|
||||
/**
|
||||
* Message time to live (ttl) for the deadletter queue. Default ttl is 3
|
||||
* weeks.
|
||||
*/
|
||||
private long ttl = Duration.ofDays(21).toMillis();
|
||||
private long ttl = Duration.ofDays(THREE_WEEKS).toMillis();
|
||||
|
||||
/**
|
||||
* Return the deadletter arguments.
|
||||
@@ -36,7 +38,7 @@ public class AmqpDeadletterProperties {
|
||||
* @return map which holds the properties
|
||||
*/
|
||||
public Map<String, Object> getDeadLetterExchangeArgs(final String exchange) {
|
||||
final Map<String, Object> args = new HashMap<>();
|
||||
final Map<String, Object> args = Maps.newHashMapWithExpectedSize(1);
|
||||
args.put("x-dead-letter-exchange", exchange);
|
||||
return args;
|
||||
}
|
||||
@@ -53,7 +55,7 @@ public class AmqpDeadletterProperties {
|
||||
}
|
||||
|
||||
private Map<String, Object> getTTLArgs() {
|
||||
final Map<String, Object> args = new HashMap<>();
|
||||
final Map<String, Object> args = Maps.newHashMapWithExpectedSize(1);
|
||||
args.put("x-message-ttl", getTtl());
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.api.ApiType;
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
|
||||
import org.eclipse.hawkbit.api.UrlProtocol;
|
||||
import org.eclipse.hawkbit.api.URLPlaceholder;
|
||||
import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
||||
@@ -25,8 +27,11 @@ import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest;
|
||||
import org.eclipse.hawkbit.dmf.json.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.eventbus.EventSubscriber;
|
||||
import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
@@ -47,6 +52,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
|
||||
private final ArtifactUrlHandler artifactUrlHandler;
|
||||
private final AmqpSenderService amqpSenderService;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final SystemManagement systemManagement;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -57,12 +64,19 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
* to send AMQP message
|
||||
* @param artifactUrlHandler
|
||||
* for generating download URLs
|
||||
* @param systemSecurityContext
|
||||
* for execution with system permissions
|
||||
* @param systemManagement
|
||||
* to access to tenant metadata
|
||||
*/
|
||||
public AmqpMessageDispatcherService(final RabbitTemplate rabbitTemplate, final AmqpSenderService amqpSenderService,
|
||||
final ArtifactUrlHandler artifactUrlHandler) {
|
||||
final ArtifactUrlHandler artifactUrlHandler, final SystemSecurityContext systemSecurityContext,
|
||||
final SystemManagement systemManagement) {
|
||||
super(rabbitTemplate);
|
||||
this.artifactUrlHandler = artifactUrlHandler;
|
||||
this.amqpSenderService = amqpSenderService;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.systemManagement = systemManagement;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,20 +88,24 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
*/
|
||||
@Subscribe
|
||||
public void targetAssignDistributionSet(final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent) {
|
||||
final URI targetAdress = targetAssignDistributionSetEvent.getTargetAdress();
|
||||
final URI targetAdress = targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress();
|
||||
if (!IpUtil.isAmqpUri(targetAdress)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String controllerId = targetAssignDistributionSetEvent.getControllerId();
|
||||
final String controllerId = targetAssignDistributionSetEvent.getTarget().getControllerId();
|
||||
final Collection<org.eclipse.hawkbit.repository.model.SoftwareModule> modules = targetAssignDistributionSetEvent
|
||||
.getSoftwareModules();
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = new DownloadAndUpdateRequest();
|
||||
downloadAndUpdateRequest.setActionId(targetAssignDistributionSetEvent.getActionId());
|
||||
downloadAndUpdateRequest.setTargetSecurityToken(targetAssignDistributionSetEvent.getTargetToken());
|
||||
|
||||
final String targetSecurityToken = systemSecurityContext
|
||||
.runAsSystem(targetAssignDistributionSetEvent.getTarget()::getSecurityToken);
|
||||
downloadAndUpdateRequest.setTargetSecurityToken(targetSecurityToken);
|
||||
|
||||
for (final org.eclipse.hawkbit.repository.model.SoftwareModule softwareModule : modules) {
|
||||
final SoftwareModule amqpSoftwareModule = convertToAmqpSoftwareModule(controllerId, softwareModule);
|
||||
final SoftwareModule amqpSoftwareModule = convertToAmqpSoftwareModule(
|
||||
targetAssignDistributionSetEvent.getTarget(), softwareModule);
|
||||
downloadAndUpdateRequest.addSoftwareModule(amqpSoftwareModule);
|
||||
}
|
||||
|
||||
@@ -133,51 +151,42 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
return messageProperties;
|
||||
}
|
||||
|
||||
private SoftwareModule convertToAmqpSoftwareModule(final String targetId,
|
||||
private SoftwareModule convertToAmqpSoftwareModule(final Target target,
|
||||
final org.eclipse.hawkbit.repository.model.SoftwareModule softwareModule) {
|
||||
final SoftwareModule amqpSoftwareModule = new SoftwareModule();
|
||||
amqpSoftwareModule.setModuleId(softwareModule.getId());
|
||||
amqpSoftwareModule.setModuleType(softwareModule.getType().getKey());
|
||||
amqpSoftwareModule.setModuleVersion(softwareModule.getVersion());
|
||||
|
||||
final List<Artifact> artifacts = convertArtifacts(targetId, softwareModule.getLocalArtifacts());
|
||||
final List<Artifact> artifacts = convertArtifacts(target, softwareModule.getLocalArtifacts());
|
||||
amqpSoftwareModule.setArtifacts(artifacts);
|
||||
return amqpSoftwareModule;
|
||||
}
|
||||
|
||||
private List<Artifact> convertArtifacts(final String targetId, final List<LocalArtifact> localArtifacts) {
|
||||
private List<Artifact> convertArtifacts(final Target target, final List<LocalArtifact> localArtifacts) {
|
||||
if (localArtifacts.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return localArtifacts.stream().map(localArtifact -> convertArtifact(targetId, localArtifact))
|
||||
return localArtifacts.stream().map(localArtifact -> convertArtifact(target, localArtifact))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Artifact convertArtifact(final String targetId, final LocalArtifact localArtifact) {
|
||||
private Artifact convertArtifact(final Target target, final LocalArtifact localArtifact) {
|
||||
final Artifact artifact = new Artifact();
|
||||
|
||||
if (artifactUrlHandler.protocolSupported(UrlProtocol.COAP)) {
|
||||
artifact.getUrls().put(Artifact.UrlProtocol.COAP,
|
||||
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
|
||||
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.COAP));
|
||||
}
|
||||
|
||||
if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTP)) {
|
||||
artifact.getUrls().put(Artifact.UrlProtocol.HTTP,
|
||||
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
|
||||
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTP));
|
||||
}
|
||||
|
||||
if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTPS)) {
|
||||
artifact.getUrls().put(Artifact.UrlProtocol.HTTPS,
|
||||
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
|
||||
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTPS));
|
||||
}
|
||||
artifact.setUrls(artifactUrlHandler
|
||||
.getUrls(new URLPlaceholder(systemManagement.getTenantMetadata().getTenant(),
|
||||
systemManagement.getTenantMetadata().getId(), target.getControllerId(), target.getId(),
|
||||
new SoftwareData(localArtifact.getSoftwareModule().getId(), localArtifact.getFilename(),
|
||||
localArtifact.getId(), localArtifact.getSha1Hash())),
|
||||
ApiType.DMF)
|
||||
.stream().collect(Collectors.toMap(e -> e.getProtocol(), e -> e.getRef())));
|
||||
|
||||
artifact.setFilename(localArtifact.getFilename());
|
||||
artifact.setHashes(new ArtifactHash(localArtifact.getSha1Hash(), localArtifact.getMd5Hash()));
|
||||
artifact.setSize(localArtifact.getSize());
|
||||
return artifact;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
package org.eclipse.hawkbit.amqp;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -18,68 +17,45 @@ import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.hawkbit.api.HostnameResolver;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
||||
import org.eclipse.hawkbit.cache.CacheConstants;
|
||||
import org.eclipse.hawkbit.cache.DownloadArtifactCache;
|
||||
import org.eclipse.hawkbit.cache.DownloadType;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
||||
import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus;
|
||||
import org.eclipse.hawkbit.dmf.json.model.Artifact;
|
||||
import org.eclipse.hawkbit.dmf.json.model.ArtifactHash;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
|
||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
|
||||
import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.messaging.handler.annotation.Header;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.CredentialsExpiredException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
* {@link AmqpMessageHandlerService} handles all incoming AMQP messages for the
|
||||
* queue which is configure for the property hawkbit.dmf.rabbitmq.receiverQueue.
|
||||
* {@link AmqpMessageHandlerService} handles all incoming target interaction
|
||||
* AMQP messages (e.g. create target, check for updates etc.) for the queue
|
||||
* which is configured for the property hawkbit.dmf.rabbitmq.receiverQueue.
|
||||
*
|
||||
*/
|
||||
public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
@@ -88,40 +64,29 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
|
||||
private final AmqpMessageDispatcherService amqpMessageDispatcherService;
|
||||
|
||||
@Autowired
|
||||
private ControllerManagement controllerManagement;
|
||||
private final ControllerManagement controllerManagement;
|
||||
|
||||
@Autowired
|
||||
private AmqpControllerAuthentication authenticationManager;
|
||||
|
||||
@Autowired
|
||||
private ArtifactManagement artifactManagement;
|
||||
|
||||
@Autowired
|
||||
@Qualifier(CacheConstants.DOWNLOAD_ID_CACHE)
|
||||
private Cache cache;
|
||||
|
||||
@Autowired
|
||||
private HostnameResolver hostnameResolver;
|
||||
|
||||
@Autowired
|
||||
private EntityFactory entityFactory;
|
||||
|
||||
@Autowired
|
||||
private SystemSecurityContext systemSecurityContext;
|
||||
private final EntityFactory entityFactory;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param defaultTemplate
|
||||
* the configured amqp template.
|
||||
* @param rabbitTemplate
|
||||
* for converting messages
|
||||
* @param amqpMessageDispatcherService
|
||||
* to sending events to DMF client
|
||||
* @param controllerManagement
|
||||
* for target repo access
|
||||
* @param entityFactory
|
||||
* to create entities
|
||||
*/
|
||||
public AmqpMessageHandlerService(final RabbitTemplate defaultTemplate,
|
||||
final AmqpMessageDispatcherService amqpMessageDispatcherService) {
|
||||
super(defaultTemplate);
|
||||
public AmqpMessageHandlerService(final RabbitTemplate rabbitTemplate,
|
||||
final AmqpMessageDispatcherService amqpMessageDispatcherService,
|
||||
final ControllerManagement controllerManagement, final EntityFactory entityFactory) {
|
||||
super(rabbitTemplate);
|
||||
this.amqpMessageDispatcherService = amqpMessageDispatcherService;
|
||||
this.controllerManagement = controllerManagement;
|
||||
this.entityFactory = entityFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,28 +107,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
return onMessage(message, type, tenant, getRabbitTemplate().getConnectionFactory().getVirtualHost());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed on a authentication request.
|
||||
*
|
||||
* @param message
|
||||
* the amqp message
|
||||
* @return the rpc message back to supplier.
|
||||
*/
|
||||
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue}", containerFactory = "listenerContainerFactory")
|
||||
public Message onAuthenticationRequest(final Message message) {
|
||||
checkContentTypeJson(message);
|
||||
final SecurityContext oldContext = SecurityContextHolder.getContext();
|
||||
try {
|
||||
return handleAuthentifiactionMessage(message);
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
|
||||
} catch (final TenantNotExistException | TooManyStatusEntriesException e) {
|
||||
throw new AmqpRejectAndDontRequeueException(e);
|
||||
} finally {
|
||||
SecurityContextHolder.setContext(oldContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* * Executed if a amqp message arrives.
|
||||
*
|
||||
@@ -206,108 +149,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Message handleAuthentifiactionMessage(final Message message) {
|
||||
final DownloadResponse authentificationResponse = new DownloadResponse();
|
||||
final MessageProperties messageProperties = message.getMessageProperties();
|
||||
final TenantSecurityToken secruityToken = convertMessage(message, TenantSecurityToken.class);
|
||||
final FileResource fileResource = secruityToken.getFileResource();
|
||||
try {
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken));
|
||||
|
||||
final LocalArtifact localArtifact = findLocalArtifactByFileResource(fileResource);
|
||||
|
||||
if (localArtifact == null) {
|
||||
LOG.info("target {} requested file resource {} which does not exists to download",
|
||||
secruityToken.getControllerId(), fileResource);
|
||||
throw new EntityNotFoundException();
|
||||
}
|
||||
|
||||
checkIfArtifactIsAssignedToTarget(secruityToken, localArtifact);
|
||||
|
||||
final Artifact artifact = convertDbArtifact(artifactManagement.loadLocalArtifactBinary(localArtifact));
|
||||
if (artifact == null) {
|
||||
throw new EntityNotFoundException();
|
||||
}
|
||||
authentificationResponse.setArtifact(artifact);
|
||||
final String downloadId = UUID.randomUUID().toString();
|
||||
// SHA1 key is set, download by SHA1
|
||||
final DownloadArtifactCache downloadCache = new DownloadArtifactCache(DownloadType.BY_SHA1,
|
||||
localArtifact.getSha1Hash());
|
||||
cache.put(downloadId, downloadCache);
|
||||
authentificationResponse
|
||||
.setDownloadUrl(UriComponentsBuilder.fromUri(hostnameResolver.resolveHostname().toURI())
|
||||
.path("/api/v1/downloadserver/downloadId/").path(downloadId).build().toUriString());
|
||||
authentificationResponse.setResponseCode(HttpStatus.OK.value());
|
||||
} catch (final BadCredentialsException | AuthenticationServiceException | CredentialsExpiredException e) {
|
||||
LOG.error("Login failed", e);
|
||||
authentificationResponse.setResponseCode(HttpStatus.FORBIDDEN.value());
|
||||
authentificationResponse.setMessage("Login failed");
|
||||
} catch (final URISyntaxException e) {
|
||||
LOG.error("URI build exception", e);
|
||||
authentificationResponse.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
authentificationResponse.setMessage("Building download URI failed");
|
||||
} catch (final EntityNotFoundException e) {
|
||||
final String errorMessage = "Artifact for resource " + fileResource + "not found ";
|
||||
LOG.warn(errorMessage, e);
|
||||
authentificationResponse.setResponseCode(HttpStatus.NOT_FOUND.value());
|
||||
authentificationResponse.setMessage(errorMessage);
|
||||
}
|
||||
|
||||
return getMessageConverter().toMessage(authentificationResponse, messageProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* check action for this download purposes, the method will throw an
|
||||
* EntityNotFoundException in case the controller is not allowed to download
|
||||
* this file because it's not assigned to an action and not assigned to this
|
||||
* controller. Otherwise no controllerId is set = anonymous download
|
||||
*
|
||||
* @param secruityToken
|
||||
* the security token which holds the target ID to check on
|
||||
* @param localArtifact
|
||||
* the local artifact to verify if the given target is allowed to
|
||||
* download this artifact
|
||||
*/
|
||||
private void checkIfArtifactIsAssignedToTarget(final TenantSecurityToken secruityToken,
|
||||
final LocalArtifact localArtifact) {
|
||||
final String controllerId = secruityToken.getControllerId();
|
||||
if (controllerId == null) {
|
||||
LOG.info("anonymous download no authentication check for artifact {}", localArtifact);
|
||||
return;
|
||||
}
|
||||
LOG.debug("no anonymous download request, doing authentication check for target {} and artifact {}",
|
||||
controllerId, localArtifact);
|
||||
if (!controllerManagement.hasTargetArtifactAssigned(controllerId, localArtifact)) {
|
||||
LOG.info("target {} tried to download artifact {} which is not assigned to the target", controllerId,
|
||||
localArtifact);
|
||||
throw new EntityNotFoundException();
|
||||
}
|
||||
LOG.info("download security check for target {} and artifact {} granted", controllerId, localArtifact);
|
||||
}
|
||||
|
||||
private LocalArtifact findLocalArtifactByFileResource(final FileResource fileResource) {
|
||||
if (fileResource.getSha1() != null) {
|
||||
return artifactManagement.findFirstLocalArtifactsBySHA1(fileResource.getSha1());
|
||||
} else if (fileResource.getFilename() != null) {
|
||||
return artifactManagement.findLocalArtifactByFilename(fileResource.getFilename()).stream().findFirst()
|
||||
.orElse(null);
|
||||
} else if (fileResource.getSoftwareModuleFilenameResource() != null) {
|
||||
return artifactManagement
|
||||
.findByFilenameAndSoftwareModule(fileResource.getSoftwareModuleFilenameResource().getFilename(),
|
||||
fileResource.getSoftwareModuleFilenameResource().getSoftwareModuleId())
|
||||
.stream().findFirst().orElse(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Artifact convertDbArtifact(final DbArtifact dbArtifact) {
|
||||
final Artifact artifact = new Artifact();
|
||||
artifact.setSize(dbArtifact.getSize());
|
||||
final DbArtifactHash dbArtifactHash = dbArtifact.getHashes();
|
||||
artifact.setHashes(new ArtifactHash(dbArtifactHash.getSha1(), dbArtifactHash.getMd5()));
|
||||
return artifact;
|
||||
}
|
||||
|
||||
private static void setSecurityContext(final Authentication authentication) {
|
||||
final SecurityContextImpl securityContextImpl = new SecurityContextImpl();
|
||||
securityContextImpl.setAuthentication(authentication);
|
||||
@@ -361,10 +202,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
final DistributionSet distributionSet = action.get().getDistributionSet();
|
||||
final List<SoftwareModule> softwareModuleList = controllerManagement
|
||||
.findSoftwareModulesByDistributionSet(distributionSet);
|
||||
final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> target.getSecurityToken());
|
||||
amqpMessageDispatcherService.targetAssignDistributionSet(new TargetAssignDistributionSetEvent(
|
||||
target.getOptLockRevision(), target.getTenant(), target.getControllerId(), action.get().getId(),
|
||||
softwareModuleList, target.getTargetInfo().getAddress(), targetSecurityToken));
|
||||
target.getOptLockRevision(), target.getTenant(), target, action.get().getId(), softwareModuleList));
|
||||
|
||||
}
|
||||
|
||||
@@ -481,7 +320,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
return action;
|
||||
}
|
||||
|
||||
private void handleCancelRejected(final Message message, final Action action, final ActionStatus actionStatus) {
|
||||
private static void handleCancelRejected(final Message message, final Action action,
|
||||
final ActionStatus actionStatus) {
|
||||
if (action.isCancelingOrCanceled()) {
|
||||
|
||||
actionStatus.setStatus(Status.WARNING);
|
||||
@@ -495,39 +335,4 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkContentTypeJson(final Message message) {
|
||||
final MessageProperties messageProperties = message.getMessageProperties();
|
||||
if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) {
|
||||
return;
|
||||
}
|
||||
throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible");
|
||||
}
|
||||
|
||||
void setControllerManagement(final ControllerManagement controllerManagement) {
|
||||
this.controllerManagement = controllerManagement;
|
||||
}
|
||||
|
||||
void setHostnameResolver(final HostnameResolver hostnameResolver) {
|
||||
this.hostnameResolver = hostnameResolver;
|
||||
}
|
||||
|
||||
void setAuthenticationManager(final AmqpControllerAuthentication authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
void setArtifactManagement(final ArtifactManagement artifactManagement) {
|
||||
this.artifactManagement = artifactManagement;
|
||||
}
|
||||
|
||||
void setCache(final Cache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
void setEntityFactory(final EntityFactory entityFactory) {
|
||||
this.entityFactory = entityFactory;
|
||||
}
|
||||
|
||||
void setSystemSecurityContext(final SystemSecurityContext systemSecurityContext) {
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,16 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@ConfigurationProperties("hawkbit.dmf.rabbitmq")
|
||||
public class AmqpProperties {
|
||||
|
||||
private static final int ONE_MINUTE = 60;
|
||||
|
||||
private static final int DEFAULT_QUEUE_DECLARATION_RETRIES = 50;
|
||||
|
||||
private static final int DEFAULT_INITIAL_CONSUMERS = 3;
|
||||
|
||||
private static final int DEFAULT_PREFETCH_COUNT = 10;
|
||||
|
||||
private static final int DEFAULT_MAX_CONSUMERS = 10;
|
||||
|
||||
/**
|
||||
* Enable DMF API based on AMQP 0.9
|
||||
*/
|
||||
@@ -54,24 +64,24 @@ public class AmqpProperties {
|
||||
/**
|
||||
* Requested heartbeat interval from broker in {@link TimeUnit#SECONDS}.
|
||||
*/
|
||||
private int requestedHeartBeat = (int) TimeUnit.SECONDS.toSeconds(60);
|
||||
private int requestedHeartBeat = (int) TimeUnit.SECONDS.toSeconds(ONE_MINUTE);
|
||||
|
||||
/**
|
||||
* Sets an upper limit to the number of consumers.
|
||||
*/
|
||||
private int maxConcurrentConsumers = 10;
|
||||
private int maxConcurrentConsumers = DEFAULT_MAX_CONSUMERS;
|
||||
|
||||
/**
|
||||
* Tells the broker how many messages to send to each consumer in a single
|
||||
* request. Often this can be set quite high to improve throughput.
|
||||
*/
|
||||
private int prefetchCount = 10;
|
||||
private int prefetchCount = DEFAULT_PREFETCH_COUNT;
|
||||
|
||||
/**
|
||||
* Initial number of consumers. Is scaled up if necessary up to
|
||||
* {@link #maxConcurrentConsumers}.
|
||||
*/
|
||||
private int initialConcurrentConsumers = 3;
|
||||
private int initialConcurrentConsumers = DEFAULT_INITIAL_CONSUMERS;
|
||||
|
||||
/**
|
||||
* The number of retry attempts when passive queue declaration fails.
|
||||
@@ -79,7 +89,7 @@ public class AmqpProperties {
|
||||
* consuming from multiple queues, when not all queues were available during
|
||||
* initialization.
|
||||
*/
|
||||
private int declarationRetries = 50;
|
||||
private int declarationRetries = DEFAULT_QUEUE_DECLARATION_RETRIES;
|
||||
|
||||
public int getDeclarationRetries() {
|
||||
return declarationRetries;
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
@@ -39,6 +40,14 @@ public class BaseAmqpService {
|
||||
this.rabbitTemplate = rabbitTemplate;
|
||||
}
|
||||
|
||||
protected static void checkContentTypeJson(final Message message) {
|
||||
final MessageProperties messageProperties = message.getMessageProperties();
|
||||
if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) {
|
||||
return;
|
||||
}
|
||||
throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible");
|
||||
}
|
||||
|
||||
/**
|
||||
* Is needed to convert a incoming message to is originally object type.
|
||||
*
|
||||
@@ -98,7 +107,7 @@ public class BaseAmqpService {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
protected final void logAndThrowMessageError(final Message message, final String error) {
|
||||
protected static final void logAndThrowMessageError(final Message message, final String error) {
|
||||
LOGGER.warn("Warning! \"{}\" reported by message: {}", error, message);
|
||||
throw new AmqpRejectAndDontRequeueException(error);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,11 @@ import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import org.eclipse.hawkbit.api.HostnameResolver;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
|
||||
@@ -23,8 +28,16 @@ import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaEntityFactory;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaLocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
|
||||
import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.DdiSecurityProperties.Authentication.Anonymous;
|
||||
import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp;
|
||||
@@ -33,11 +46,15 @@ import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -54,15 +71,44 @@ import ru.yandex.qatools.allure.annotations.Stories;
|
||||
*/
|
||||
@Features("Component Tests - Device Management Federation API")
|
||||
@Stories("AmqpController Authentication Test")
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AmqpControllerAuthenticationTest {
|
||||
|
||||
private static final String SHA1 = "12345";
|
||||
private static final Long ARTIFACT_ID = 1123L;
|
||||
private static final Long ARTIFACT_SIZE = 6666L;
|
||||
private static final String TENANT = "DEFAULT";
|
||||
private static String CONTROLLLER_ID = "123";
|
||||
private static final Long TENANT_ID = 123L;
|
||||
private static final String CONTROLLER_ID = "123";
|
||||
private static final Long TARGET_ID = 123L;
|
||||
private AmqpMessageHandlerService amqpMessageHandlerService;
|
||||
private AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandlerService;
|
||||
|
||||
private MessageConverter messageConverter;
|
||||
private TenantConfigurationManagement tenantConfigurationManagement;
|
||||
|
||||
private AmqpControllerAuthentication authenticationManager;
|
||||
|
||||
@Mock
|
||||
private TenantConfigurationManagement tenantConfigurationManagementMock;
|
||||
|
||||
@Mock
|
||||
private SystemManagement systemManagement;
|
||||
|
||||
@Mock
|
||||
private Cache cacheMock;
|
||||
|
||||
@Mock
|
||||
private HostnameResolver hostnameResolverMock;
|
||||
|
||||
@Mock
|
||||
private ArtifactManagement artifactManagementMock;
|
||||
|
||||
@Mock
|
||||
private ControllerManagement controllerManagementMock;
|
||||
|
||||
@Mock
|
||||
private Target targteMock;
|
||||
|
||||
private static final TenantConfigurationValue<Boolean> CONFIG_VALUE_FALSE = TenantConfigurationValue
|
||||
.<Boolean> builder().value(Boolean.FALSE).build();
|
||||
|
||||
@@ -74,8 +120,6 @@ public class AmqpControllerAuthenticationTest {
|
||||
messageConverter = new Jackson2JsonMessageConverter();
|
||||
final RabbitTemplate rabbitTemplate = mock(RabbitTemplate.class);
|
||||
when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
|
||||
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate,
|
||||
mock(AmqpMessageDispatcherService.class));
|
||||
|
||||
final DdiSecurityProperties secruityProperties = mock(DdiSecurityProperties.class);
|
||||
final Rp rp = mock(Rp.class);
|
||||
@@ -88,30 +132,57 @@ public class AmqpControllerAuthenticationTest {
|
||||
when(ddiAuthentication.getAnonymous()).thenReturn(anonymous);
|
||||
when(anonymous.isEnabled()).thenReturn(false);
|
||||
|
||||
tenantConfigurationManagement = mock(TenantConfigurationManagement.class);
|
||||
|
||||
when(tenantConfigurationManagement.getConfigurationValue(any(), eq(Boolean.class)))
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(any(), eq(Boolean.class)))
|
||||
.thenReturn(CONFIG_VALUE_FALSE);
|
||||
|
||||
final ControllerManagement controllerManagement = mock(ControllerManagement.class);
|
||||
when(controllerManagement.getSecurityTokenByControllerId(anyString())).thenReturn(CONTROLLLER_ID);
|
||||
amqpMessageHandlerService.setArtifactManagement(mock(ArtifactManagement.class));
|
||||
when(controllerManagement.findByControllerId(anyString())).thenReturn(targteMock);
|
||||
when(controllerManagement.findByTargetId(any(Long.class))).thenReturn(targteMock);
|
||||
|
||||
when(targteMock.getSecurityToken()).thenReturn(CONTROLLER_ID);
|
||||
when(targteMock.getControllerId()).thenReturn(CONTROLLER_ID);
|
||||
|
||||
final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware();
|
||||
final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware);
|
||||
|
||||
authenticationManager = new AmqpControllerAuthentication(controllerManagement, tenantConfigurationManagement,
|
||||
tenantAware, secruityProperties, systemSecurityContext);
|
||||
final TenantMetaData tenantMetaData = mock(TenantMetaData.class);
|
||||
when(tenantMetaData.getTenant()).thenReturn(TENANT);
|
||||
when(systemManagement.getTenantMetadata(TENANT_ID)).thenReturn(tenantMetaData);
|
||||
|
||||
authenticationManager = new AmqpControllerAuthentication(systemManagement, controllerManagement,
|
||||
tenantConfigurationManagementMock, tenantAware, secruityProperties, systemSecurityContext);
|
||||
|
||||
authenticationManager.postConstruct();
|
||||
amqpMessageHandlerService.setAuthenticationManager(authenticationManager);
|
||||
|
||||
final LocalArtifact testArtifact = new JpaLocalArtifact("afilename", "afilename", new JpaSoftwareModule(
|
||||
new JpaSoftwareModuleType("a key", "a name", null, 1), "a name", null, null, null));
|
||||
|
||||
when(artifactManagementMock.findLocalArtifact(ARTIFACT_ID)).thenReturn(testArtifact);
|
||||
when(artifactManagementMock.findFirstLocalArtifactsBySHA1(SHA1)).thenReturn(testArtifact);
|
||||
|
||||
final DbArtifact artifact = new DbArtifact();
|
||||
artifact.setSize(ARTIFACT_SIZE);
|
||||
artifact.setHashes(new DbArtifactHash("sha1 test", "md5 test"));
|
||||
when(artifactManagementMock.loadLocalArtifactBinary(testArtifact)).thenReturn(artifact);
|
||||
|
||||
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate,
|
||||
mock(AmqpMessageDispatcherService.class), controllerManagementMock, new JpaEntityFactory());
|
||||
|
||||
amqpAuthenticationMessageHandlerService = new AmqpAuthenticationMessageHandler(rabbitTemplate,
|
||||
authenticationManager, artifactManagementMock, cacheMock, hostnameResolverMock,
|
||||
controllerManagementMock);
|
||||
|
||||
when(hostnameResolverMock.resolveHostname()).thenReturn(new URL("http://localhost"));
|
||||
|
||||
when(controllerManagementMock.hasTargetArtifactAssigned(TARGET_ID, testArtifact)).thenReturn(true);
|
||||
when(controllerManagementMock.hasTargetArtifactAssigned(CONTROLLER_ID, testArtifact)).thenReturn(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests authentication manager without principal")
|
||||
public void testAuthenticationeBadCredantialsWithoutPricipal() {
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
|
||||
FileResource.createFileResourceBySha1("12345"));
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID,
|
||||
FileResource.createFileResourceBySha1(SHA1));
|
||||
try {
|
||||
authenticationManager.doAuthenticate(securityToken);
|
||||
fail("BadCredentialsException was excepeted since principal was missing");
|
||||
@@ -124,12 +195,12 @@ public class AmqpControllerAuthenticationTest {
|
||||
@Test
|
||||
@Description("Tests authentication manager without wrong credential")
|
||||
public void testAuthenticationBadCredantialsWithWrongCredential() {
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
|
||||
FileResource.createFileResourceBySha1("12345"));
|
||||
when(tenantConfigurationManagement.getConfigurationValue(
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID,
|
||||
FileResource.createFileResourceBySha1(SHA1));
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
|
||||
.thenReturn(CONFIG_VALUE_TRUE);
|
||||
securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLLER_ID);
|
||||
securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLER_ID);
|
||||
try {
|
||||
authenticationManager.doAuthenticate(securityToken);
|
||||
fail("BadCredentialsException was excepeted due to wrong credential");
|
||||
@@ -142,12 +213,12 @@ public class AmqpControllerAuthenticationTest {
|
||||
@Test
|
||||
@Description("Tests authentication successfull")
|
||||
public void testSuccessfullAuthentication() {
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
|
||||
FileResource.createFileResourceBySha1("12345"));
|
||||
when(tenantConfigurationManagement.getConfigurationValue(
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID,
|
||||
FileResource.createFileResourceBySha1(SHA1));
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
|
||||
.thenReturn(CONFIG_VALUE_TRUE);
|
||||
securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLLER_ID);
|
||||
securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID);
|
||||
final Authentication authentication = authenticationManager.doAuthenticate(securityToken);
|
||||
assertThat(authentication).isNotNull();
|
||||
}
|
||||
@@ -157,13 +228,13 @@ public class AmqpControllerAuthenticationTest {
|
||||
public void testAuthenticationMessageBadCredantialsWithoutPricipal() {
|
||||
final MessageProperties messageProperties = createMessageProperties(null);
|
||||
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
|
||||
FileResource.createFileResourceBySha1("12345"));
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID,
|
||||
FileResource.createFileResourceBySha1(SHA1));
|
||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
||||
messageProperties);
|
||||
|
||||
// test
|
||||
final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message);
|
||||
final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message);
|
||||
|
||||
// verify
|
||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||
@@ -175,17 +246,17 @@ public class AmqpControllerAuthenticationTest {
|
||||
@Description("Tests authentication message without wrong credential")
|
||||
public void testAuthenticationMessageBadCredantialsWithWrongCredential() {
|
||||
final MessageProperties messageProperties = createMessageProperties(null);
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
|
||||
FileResource.createFileResourceBySha1("12345"));
|
||||
when(tenantConfigurationManagement.getConfigurationValue(
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID,
|
||||
FileResource.createFileResourceBySha1(SHA1));
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
|
||||
.thenReturn(CONFIG_VALUE_TRUE);
|
||||
securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLLER_ID);
|
||||
securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLER_ID);
|
||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
||||
messageProperties);
|
||||
|
||||
// test
|
||||
final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message);
|
||||
final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message);
|
||||
|
||||
// verify
|
||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||
@@ -195,24 +266,81 @@ public class AmqpControllerAuthenticationTest {
|
||||
|
||||
@Test
|
||||
@Description("Tests authentication message successfull")
|
||||
public void testSuccessfullMessageAuthentication() {
|
||||
public void successfullMessageAuthentication() {
|
||||
final MessageProperties messageProperties = createMessageProperties(null);
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
|
||||
FileResource.createFileResourceBySha1("12345"));
|
||||
when(tenantConfigurationManagement.getConfigurationValue(
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, null, CONTROLLER_ID, null,
|
||||
FileResource.createFileResourceBySha1(SHA1));
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
|
||||
.thenReturn(CONFIG_VALUE_TRUE);
|
||||
securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLLER_ID);
|
||||
securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID);
|
||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
||||
messageProperties);
|
||||
|
||||
// test
|
||||
final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message);
|
||||
final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message);
|
||||
|
||||
// verify
|
||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||
assertThat(downloadResponse).isNotNull();
|
||||
assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.NOT_FOUND.value());
|
||||
assertThat(downloadResponse.getDownloadUrl()).isNotNull();
|
||||
assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value());
|
||||
assertThat(SecurityContextHolder.getContext()).isNotNull();
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication().getClass().getName())
|
||||
.isEqualTo(PreAuthenticatedAuthenticationToken.class.getName());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests authentication message successfull with targetId intead of controllerId provided and artifactId instead of SHA1.")
|
||||
public void successfullMessageAuthenticationWithTargetIdAndArtifactId() {
|
||||
final MessageProperties messageProperties = createMessageProperties(null);
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, null, null, TARGET_ID,
|
||||
FileResource.createFileResourceByArtifactId(ARTIFACT_ID));
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
|
||||
.thenReturn(CONFIG_VALUE_TRUE);
|
||||
securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID);
|
||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
||||
messageProperties);
|
||||
|
||||
// test
|
||||
final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message);
|
||||
|
||||
// verify
|
||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||
assertThat(downloadResponse).isNotNull();
|
||||
assertThat(downloadResponse.getDownloadUrl()).isNotNull();
|
||||
assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value());
|
||||
assertThat(SecurityContextHolder.getContext()).isNotNull();
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication().getClass().getName())
|
||||
.isEqualTo(PreAuthenticatedAuthenticationToken.class.getName());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests authentication message successfull")
|
||||
public void successfullMessageAuthenticationWithTenantid() {
|
||||
final MessageProperties messageProperties = createMessageProperties(null);
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(null, TENANT_ID, CONTROLLER_ID, TARGET_ID,
|
||||
FileResource.createFileResourceBySha1(SHA1));
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(
|
||||
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
|
||||
.thenReturn(CONFIG_VALUE_TRUE);
|
||||
securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID);
|
||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
||||
messageProperties);
|
||||
|
||||
// test
|
||||
final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message);
|
||||
|
||||
// verify
|
||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||
assertThat(downloadResponse).isNotNull();
|
||||
assertThat(downloadResponse.getDownloadUrl()).isNotNull();
|
||||
assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value());
|
||||
assertThat(SecurityContextHolder.getContext()).isNotNull();
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication().getClass().getName())
|
||||
|
||||
@@ -13,9 +13,7 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyLong;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -24,6 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.api.ArtifactUrl;
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
|
||||
@@ -31,11 +30,14 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest;
|
||||
import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.junit.Test;
|
||||
@@ -49,6 +51,8 @@ import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
@@ -60,6 +64,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
|
||||
public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
||||
|
||||
private static final String TENANT = "default";
|
||||
private static final Long TENANT_ID = 4711L;
|
||||
|
||||
private static final URI AMQP_URI = IpUtil.createAmqpUri("vHost", "mytest");
|
||||
|
||||
@@ -71,31 +76,47 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
||||
|
||||
private DefaultAmqpSenderService senderService;
|
||||
|
||||
private SystemManagement systemManagement;
|
||||
|
||||
private static final String CONTROLLER_ID = "1";
|
||||
|
||||
private Target testTarget;
|
||||
|
||||
@Override
|
||||
public void before() throws Exception {
|
||||
super.before();
|
||||
testTarget = entityFactory.generateTarget(CONTROLLER_ID, TEST_TOKEN);
|
||||
testTarget.getTargetInfo().setAddress(AMQP_URI.toString());
|
||||
|
||||
this.rabbitTemplate = Mockito.mock(RabbitTemplate.class);
|
||||
when(rabbitTemplate.getMessageConverter()).thenReturn(new Jackson2JsonMessageConverter());
|
||||
|
||||
senderService = Mockito.mock(DefaultAmqpSenderService.class);
|
||||
|
||||
final ArtifactUrlHandler artifactUrlHandlerMock = Mockito.mock(ArtifactUrlHandler.class);
|
||||
when(artifactUrlHandlerMock.getUrl(anyString(), anyLong(), anyString(), anyString(), anyObject()))
|
||||
.thenReturn("http://mockurl");
|
||||
when(artifactUrlHandlerMock.getUrls(anyObject(), anyObject()))
|
||||
.thenReturn(Lists.newArrayList(new ArtifactUrl("http", "download", "http://mockurl")));
|
||||
|
||||
systemManagement = Mockito.mock(SystemManagement.class);
|
||||
final TenantMetaData tenantMetaData = Mockito.mock(TenantMetaData.class);
|
||||
when(tenantMetaData.getId()).thenReturn(TENANT_ID);
|
||||
when(tenantMetaData.getTenant()).thenReturn(TENANT);
|
||||
|
||||
when(systemManagement.getTenantMetadata()).thenReturn(tenantMetaData);
|
||||
|
||||
amqpMessageDispatcherService = new AmqpMessageDispatcherService(rabbitTemplate, senderService,
|
||||
artifactUrlHandlerMock);
|
||||
artifactUrlHandlerMock, systemSecurityContext, systemManagement);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verfies that download and install event with no software modul works")
|
||||
public void testSendDownloadRequesWithEmptySoftwareModules() {
|
||||
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
|
||||
1L, TENANT, CONTROLLER_ID, 1L, new ArrayList<SoftwareModule>(), AMQP_URI, TEST_TOKEN);
|
||||
1L, TENANT, testTarget, 1L, new ArrayList<SoftwareModule>());
|
||||
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
|
||||
final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
|
||||
final Message sendMessage = createArgumentCapture(
|
||||
targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress());
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
|
||||
assertThat(downloadAndUpdateRequest.getTargetSecurityToken()).isEqualTo(TEST_TOKEN);
|
||||
assertTrue("No softwaremmodule should be contained in the request",
|
||||
@@ -107,9 +128,10 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
||||
public void testSendDownloadRequesWithSoftwareModulesAndNoArtifacts() {
|
||||
final DistributionSet dsA = testdataFactory.createDistributionSet("");
|
||||
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
|
||||
1L, TENANT, CONTROLLER_ID, 1L, dsA.getModules(), AMQP_URI, TEST_TOKEN);
|
||||
1L, TENANT, testTarget, 1L, dsA.getModules());
|
||||
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
|
||||
final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
|
||||
final Message sendMessage = createArgumentCapture(
|
||||
targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress());
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
|
||||
assertEquals("Expecting a size of 3 software modules in the reuqest", 3,
|
||||
downloadAndUpdateRequest.getSoftwareModules().size());
|
||||
@@ -146,9 +168,10 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
||||
Mockito.when(rabbitTemplate.convertSendAndReceive(any())).thenReturn(receivedList);
|
||||
|
||||
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
|
||||
1L, TENANT, CONTROLLER_ID, 1L, dsA.getModules(), AMQP_URI, TEST_TOKEN);
|
||||
1L, TENANT, testTarget, 1L, dsA.getModules());
|
||||
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
|
||||
final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
|
||||
final Message sendMessage = createArgumentCapture(
|
||||
targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress());
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
|
||||
assertEquals("DownloadAndUpdateRequest event should contains 3 software modules", 3,
|
||||
downloadAndUpdateRequest.getSoftwareModules().size());
|
||||
|
||||
@@ -54,7 +54,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -81,8 +80,12 @@ import ru.yandex.qatools.allure.annotations.Stories;
|
||||
public class AmqpMessageHandlerServiceTest {
|
||||
|
||||
private static final String TENANT = "DEFAULT";
|
||||
private static final Long TENANT_ID = 123L;
|
||||
private static String CONTROLLLER_ID = "123";
|
||||
private static final Long TARGET_ID = 123L;
|
||||
|
||||
private AmqpMessageHandlerService amqpMessageHandlerService;
|
||||
private AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandlerService;
|
||||
|
||||
private MessageConverter messageConverter;
|
||||
|
||||
@@ -113,22 +116,16 @@ public class AmqpMessageHandlerServiceTest {
|
||||
@Mock
|
||||
private RabbitTemplate rabbitTemplate;
|
||||
|
||||
@Mock
|
||||
private SystemSecurityContext systemSecurityContextMock;
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
messageConverter = new Jackson2JsonMessageConverter();
|
||||
when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
|
||||
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock);
|
||||
amqpMessageHandlerService.setControllerManagement(controllerManagementMock);
|
||||
amqpMessageHandlerService.setAuthenticationManager(authenticationManagerMock);
|
||||
amqpMessageHandlerService.setArtifactManagement(artifactManagementMock);
|
||||
amqpMessageHandlerService.setCache(cacheMock);
|
||||
amqpMessageHandlerService.setHostnameResolver(hostnameResolverMock);
|
||||
amqpMessageHandlerService.setEntityFactory(entityFactoryMock);
|
||||
amqpMessageHandlerService.setSystemSecurityContext(systemSecurityContextMock);
|
||||
|
||||
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock,
|
||||
controllerManagementMock, entityFactoryMock);
|
||||
amqpAuthenticationMessageHandlerService = new AmqpAuthenticationMessageHandler(rabbitTemplate,
|
||||
authenticationManagerMock, artifactManagementMock, cacheMock, hostnameResolverMock,
|
||||
controllerManagementMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -279,13 +276,13 @@ public class AmqpMessageHandlerServiceTest {
|
||||
@Description("Tests that an download request is denied for an artifact which does not exists")
|
||||
public void authenticationRequestDeniedForArtifactWhichDoesNotExists() {
|
||||
final MessageProperties messageProperties = createMessageProperties(null);
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123",
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLLER_ID, TARGET_ID,
|
||||
FileResource.createFileResourceBySha1("12345"));
|
||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
||||
messageProperties);
|
||||
|
||||
// test
|
||||
final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message);
|
||||
final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message);
|
||||
|
||||
// verify
|
||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||
@@ -298,7 +295,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
@Description("Tests that an download request is denied for an artifact which is not assigned to the requested target")
|
||||
public void authenticationRequestDeniedForArtifactWhichIsNotAssignedToTarget() {
|
||||
final MessageProperties messageProperties = createMessageProperties(null);
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123",
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLLER_ID, TARGET_ID,
|
||||
FileResource.createFileResourceBySha1("12345"));
|
||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
||||
messageProperties);
|
||||
@@ -309,7 +306,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
.thenThrow(EntityNotFoundException.class);
|
||||
|
||||
// test
|
||||
final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message);
|
||||
final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message);
|
||||
|
||||
// verify
|
||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||
@@ -322,7 +319,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
@Description("Tests that an download request is allowed for an artifact which exists and assigned to the requested target")
|
||||
public void authenticationRequestAllowedForArtifactWhichExistsAndAssignedToTarget() throws MalformedURLException {
|
||||
final MessageProperties messageProperties = createMessageProperties(null);
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123",
|
||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLLER_ID, TARGET_ID,
|
||||
FileResource.createFileResourceBySha1("12345"));
|
||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
||||
messageProperties);
|
||||
@@ -340,7 +337,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
when(hostnameResolverMock.resolveHostname()).thenReturn(new URL("http://localhost"));
|
||||
|
||||
// test
|
||||
final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message);
|
||||
final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message);
|
||||
|
||||
// verify
|
||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||
@@ -364,15 +361,12 @@ public class AmqpMessageHandlerServiceTest {
|
||||
when(controllerManagementMock.addUpdateActionStatus(Matchers.any())).thenReturn(action);
|
||||
when(entityFactoryMock.generateActionStatus()).thenReturn(new JpaActionStatus());
|
||||
// for the test the same action can be used
|
||||
when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any()))
|
||||
.thenReturn(Optional.of(action));
|
||||
when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any())).thenReturn(Optional.of(action));
|
||||
|
||||
final List<SoftwareModule> softwareModuleList = createSoftwareModuleList();
|
||||
when(controllerManagementMock.findSoftwareModulesByDistributionSet(Matchers.any()))
|
||||
.thenReturn(softwareModuleList);
|
||||
|
||||
when(systemSecurityContextMock.runAsSystem(anyObject())).thenReturn("securityToken");
|
||||
|
||||
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
||||
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
||||
final ActionUpdateStatus actionUpdateStatus = createActionUpdateStatus(ActionStatus.FINISHED, 23L);
|
||||
@@ -393,10 +387,10 @@ public class AmqpMessageHandlerServiceTest {
|
||||
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = captorTargetAssignDistributionSetEvent
|
||||
.getValue();
|
||||
|
||||
assertThat(targetAssignDistributionSetEvent.getControllerId()).as("event has wrong controller id")
|
||||
assertThat(targetAssignDistributionSetEvent.getTarget().getControllerId()).as("event has wrong controller id")
|
||||
.isEqualTo("target1");
|
||||
assertThat(targetAssignDistributionSetEvent.getTargetToken()).as("targetoken not filled correctly")
|
||||
.isEqualTo(action.getTarget().getSecurityToken());
|
||||
assertThat(targetAssignDistributionSetEvent.getTarget().getSecurityToken())
|
||||
.as("targetoken not filled correctly").isEqualTo(action.getTarget().getSecurityToken());
|
||||
assertThat(targetAssignDistributionSetEvent.getActionId()).as("event has wrong action id").isEqualTo(22L);
|
||||
assertThat(targetAssignDistributionSetEvent.getSoftwareModules()).as("event has wrong sofware modules")
|
||||
.isEqualTo(softwareModuleList);
|
||||
|
||||
@@ -52,8 +52,8 @@ public class BaseAmqpServiceTest {
|
||||
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
|
||||
actionUpdateStatus.setActionId(1L);
|
||||
actionUpdateStatus.setSoftwareModuleId(2L);
|
||||
actionUpdateStatus.getMessage().add("Message 1");
|
||||
actionUpdateStatus.getMessage().add("Message 2");
|
||||
actionUpdateStatus.addMessage("Message 1");
|
||||
actionUpdateStatus.addMessage("Message 2");
|
||||
|
||||
final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatus,
|
||||
new MessageProperties());
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
/**
|
||||
* 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.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.eclipse.hawkbit.AmqpTestConfiguration;
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
|
||||
import org.eclipse.hawkbit.api.UrlProtocol;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
/**
|
||||
* Tests for creating urls to download artifacts.
|
||||
*/
|
||||
@Features("Component Tests - Artifact URL Handler")
|
||||
@Stories("Test to generate the artifact download URL")
|
||||
@SpringApplicationConfiguration(classes = { AmqpTestConfiguration.class,
|
||||
org.eclipse.hawkbit.RepositoryApplicationConfiguration.class })
|
||||
public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest {
|
||||
|
||||
private static final String HTTPS_LOCALHOST = "https://localhost/";
|
||||
private static final String HTTP_LOCALHOST = "http://localhost/";
|
||||
|
||||
@Autowired
|
||||
private ArtifactUrlHandler urlHandlerProperties;
|
||||
|
||||
private LocalArtifact localArtifact;
|
||||
private static final String CONTROLLER_ID = "Test";
|
||||
private String fileName;
|
||||
private Long softwareModuleId;
|
||||
private String sha1Hash;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
final DistributionSet dsA = testdataFactory.createDistributionSet("");
|
||||
final SoftwareModule module = dsA.getModules().iterator().next();
|
||||
localArtifact = testdataFactory.createLocalArtifacts(module.getId()).stream().findAny().get();
|
||||
softwareModuleId = localArtifact.getSoftwareModule().getId();
|
||||
fileName = localArtifact.getFilename();
|
||||
sha1Hash = localArtifact.getSha1Hash();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests the generation of http download url.")
|
||||
public void testHttpUrl() {
|
||||
|
||||
final String url = urlHandlerProperties.getUrl(CONTROLLER_ID, softwareModuleId, fileName, sha1Hash,
|
||||
UrlProtocol.HTTP);
|
||||
assertEquals("http is build incorrect",
|
||||
HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + CONTROLLER_ID
|
||||
+ "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/"
|
||||
+ localArtifact.getFilename(),
|
||||
url);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests the generation of https download url.")
|
||||
public void testHttpsUrl() {
|
||||
final String url = urlHandlerProperties.getUrl(CONTROLLER_ID, softwareModuleId, fileName, sha1Hash,
|
||||
UrlProtocol.HTTPS);
|
||||
assertEquals("https is build incorrect",
|
||||
HTTPS_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + CONTROLLER_ID
|
||||
+ "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/"
|
||||
+ localArtifact.getFilename(),
|
||||
url);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests the generation of coap download url.")
|
||||
public void testCoapUrl() {
|
||||
final String url = urlHandlerProperties.getUrl(CONTROLLER_ID, softwareModuleId, fileName, sha1Hash,
|
||||
UrlProtocol.COAP);
|
||||
|
||||
assertEquals("coap is build incorrect", "coap://127.0.0.1:5683/fw/" + tenantAware.getCurrentTenant() + "/"
|
||||
+ CONTROLLER_ID + "/sha1/" + localArtifact.getSha1Hash(), url);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
package org.eclipse.hawkbit.dmf.json.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
@@ -18,9 +20,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* JSON representation of action update status.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@@ -32,7 +31,7 @@ public class ActionUpdateStatus {
|
||||
@JsonProperty(required = true)
|
||||
private ActionStatus actionStatus;
|
||||
@JsonProperty
|
||||
private final List<String> message = new ArrayList<>();
|
||||
private List<String> message;
|
||||
|
||||
public Long getActionId() {
|
||||
return actionId;
|
||||
@@ -59,7 +58,32 @@ public class ActionUpdateStatus {
|
||||
}
|
||||
|
||||
public List<String> getMessage() {
|
||||
return message;
|
||||
if (message == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(message);
|
||||
}
|
||||
|
||||
public boolean addMessage(final String message) {
|
||||
if (this.message == null) {
|
||||
this.message = new ArrayList<>();
|
||||
}
|
||||
|
||||
return this.message.add(message);
|
||||
}
|
||||
|
||||
public boolean addMessage(final Collection<String> messages) {
|
||||
if (messages == null || messages.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.message == null) {
|
||||
this.message = new ArrayList<>(messages);
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.message.addAll(messages);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.dmf.json.model;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
@@ -25,15 +25,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Artifact {
|
||||
|
||||
/**
|
||||
* Represented the supported protocols for artifact url's.
|
||||
*
|
||||
*/
|
||||
public enum UrlProtocol {
|
||||
COAP, HTTP, HTTPS
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
private String filename;
|
||||
|
||||
@@ -44,13 +35,17 @@ public class Artifact {
|
||||
private Long size;
|
||||
|
||||
@JsonProperty
|
||||
private Map<UrlProtocol, String> urls = new EnumMap<>(UrlProtocol.class);
|
||||
private Map<String, String> urls;
|
||||
|
||||
public Map<UrlProtocol, String> getUrls() {
|
||||
return urls;
|
||||
public Map<String, String> getUrls() {
|
||||
if (urls == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(urls);
|
||||
}
|
||||
|
||||
public void setUrls(final Map<UrlProtocol, String> urls) {
|
||||
public void setUrls(final Map<String, String> urls) {
|
||||
this.urls = urls;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.dmf.json.model;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
@@ -30,7 +31,7 @@ public class DownloadAndUpdateRequest {
|
||||
private String targetSecurityToken;
|
||||
|
||||
@JsonProperty
|
||||
private final List<SoftwareModule> softwareModules = new LinkedList<>();
|
||||
private List<SoftwareModule> softwareModules;
|
||||
|
||||
public Long getActionId() {
|
||||
return actionId;
|
||||
@@ -49,7 +50,11 @@ public class DownloadAndUpdateRequest {
|
||||
}
|
||||
|
||||
public List<SoftwareModule> getSoftwareModules() {
|
||||
return softwareModules;
|
||||
if (softwareModules == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(softwareModules);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,6 +64,10 @@ public class DownloadAndUpdateRequest {
|
||||
* the module
|
||||
*/
|
||||
public void addSoftwareModule(final SoftwareModule createSoftwareModule) {
|
||||
if (softwareModules == null) {
|
||||
softwareModules = new ArrayList<>();
|
||||
}
|
||||
|
||||
softwareModules.add(createSoftwareModule);
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.dmf.json.model;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
@@ -35,7 +35,7 @@ public class SoftwareModule {
|
||||
@JsonProperty
|
||||
private String moduleVersion;
|
||||
@JsonProperty
|
||||
private List<Artifact> artifacts = new LinkedList<>();
|
||||
private List<Artifact> artifacts;
|
||||
|
||||
public String getModuleType() {
|
||||
return moduleType;
|
||||
@@ -54,7 +54,11 @@ public class SoftwareModule {
|
||||
}
|
||||
|
||||
public List<Artifact> getArtifacts() {
|
||||
return artifacts;
|
||||
if (artifacts == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(artifacts);
|
||||
}
|
||||
|
||||
public Long getModuleId() {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.dmf.json.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@@ -27,16 +28,47 @@ public class TenantSecurityToken {
|
||||
|
||||
public static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
|
||||
@JsonProperty
|
||||
private final String tenant;
|
||||
@JsonProperty
|
||||
@JsonProperty(required = false)
|
||||
private String tenant;
|
||||
@JsonProperty(required = false)
|
||||
private final Long tenantId;
|
||||
@JsonProperty(required = false)
|
||||
private final String controllerId;
|
||||
@JsonProperty(required = false)
|
||||
private Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
private final Long targetId;
|
||||
|
||||
@JsonProperty(required = false)
|
||||
private Map<String, String> headers;
|
||||
|
||||
@JsonProperty(required = false)
|
||||
private final FileResource fileResource;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param tenant
|
||||
* the tenant for the security token
|
||||
* @param tenantId
|
||||
* alternative tenant identification by technical ID
|
||||
* @param controllerId
|
||||
* the ID of the controller for the security token
|
||||
* @param targetId
|
||||
* alternative target identification by technical ID
|
||||
* @param fileResource
|
||||
* the file to obtain
|
||||
*/
|
||||
@JsonCreator
|
||||
public TenantSecurityToken(@JsonProperty("tenant") final String tenant,
|
||||
@JsonProperty("tenantId") final Long tenantId, @JsonProperty("controllerId") final String controllerId,
|
||||
@JsonProperty("targetId") final Long targetId,
|
||||
@JsonProperty("fileResource") final FileResource fileResource) {
|
||||
this.tenant = tenant;
|
||||
this.tenantId = tenantId;
|
||||
this.controllerId = controllerId;
|
||||
this.targetId = targetId;
|
||||
this.fileResource = fileResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -47,13 +79,26 @@ public class TenantSecurityToken {
|
||||
* @param fileResource
|
||||
* the file to obtain
|
||||
*/
|
||||
@JsonCreator
|
||||
public TenantSecurityToken(@JsonProperty("tenant") final String tenant,
|
||||
@JsonProperty("controllerId") final String controllerId,
|
||||
@JsonProperty("fileResource") final FileResource fileResource) {
|
||||
public TenantSecurityToken(final String tenant, final String controllerId, final FileResource fileResource) {
|
||||
this(tenant, null, controllerId, null, fileResource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param tenantId
|
||||
* the tenant for the security token
|
||||
* @param targetId
|
||||
* target identification by technical ID
|
||||
* @param fileResource
|
||||
* the file to obtain
|
||||
*/
|
||||
public TenantSecurityToken(final Long tenantId, final Long targetId, final FileResource fileResource) {
|
||||
this(null, tenantId, null, targetId, fileResource);
|
||||
}
|
||||
|
||||
public void setTenant(final String tenant) {
|
||||
this.tenant = tenant;
|
||||
this.controllerId = controllerId;
|
||||
this.fileResource = fileResource;
|
||||
}
|
||||
|
||||
public String getTenant() {
|
||||
@@ -65,13 +110,25 @@ public class TenantSecurityToken {
|
||||
}
|
||||
|
||||
public Map<String, String> getHeaders() {
|
||||
return headers;
|
||||
if (headers == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(headers);
|
||||
}
|
||||
|
||||
public FileResource getFileResource() {
|
||||
return fileResource;
|
||||
}
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public Long getTargetId() {
|
||||
return targetId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a header value.
|
||||
*
|
||||
@@ -80,6 +137,10 @@ public class TenantSecurityToken {
|
||||
* @return the value
|
||||
*/
|
||||
public String getHeader(final String name) {
|
||||
if (headers == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return headers.get(name);
|
||||
}
|
||||
|
||||
@@ -88,6 +149,24 @@ public class TenantSecurityToken {
|
||||
this.headers.putAll(headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates the specified header value with the specified name.
|
||||
*
|
||||
* @param name
|
||||
* of the header
|
||||
* @param value
|
||||
* of the header
|
||||
*
|
||||
* @return the previous value associated with the <tt>name</tt>, or
|
||||
* <tt>null</tt> if there was no mapping for <tt>name</tt>.
|
||||
*/
|
||||
public String putHeader(final String name, final String value) {
|
||||
if (headers == null) {
|
||||
headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
}
|
||||
return headers.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* File resource descriptor which is used to ask for the resource to
|
||||
* download e.g. The lookup of the file can be different e.g. by SHA1 hash
|
||||
@@ -99,6 +178,8 @@ public class TenantSecurityToken {
|
||||
@JsonProperty(required = false)
|
||||
private String sha1;
|
||||
@JsonProperty(required = false)
|
||||
private Long artifactId;
|
||||
@JsonProperty(required = false)
|
||||
private String filename;
|
||||
@JsonProperty(required = false)
|
||||
private SoftwareModuleFilenameResource softwareModuleFilenameResource;
|
||||
@@ -128,6 +209,14 @@ public class TenantSecurityToken {
|
||||
this.softwareModuleFilenameResource = softwareModuleFilenameResource;
|
||||
}
|
||||
|
||||
public Long getArtifactId() {
|
||||
return artifactId;
|
||||
}
|
||||
|
||||
public void setArtifactId(final Long artifactId) {
|
||||
this.artifactId = artifactId;
|
||||
}
|
||||
|
||||
/**
|
||||
* factory method to create a file resource for an SHA1 lookup.
|
||||
*
|
||||
@@ -141,6 +230,19 @@ public class TenantSecurityToken {
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* factory method to create a file resource for an artifact ID lookup.
|
||||
*
|
||||
* @param artifactId
|
||||
* the artifact IF key of the file to obtain
|
||||
* @return the {@link FileResource} with SHA1 key set
|
||||
*/
|
||||
public static FileResource createFileResourceByArtifactId(final Long artifactId) {
|
||||
final FileResource resource = new FileResource();
|
||||
resource.artifactId = artifactId;
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* factory method to create a file resource for an filename lookup.
|
||||
*
|
||||
@@ -173,7 +275,7 @@ public class TenantSecurityToken {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FileResource [sha1=" + sha1 + ", filename=" + filename + "]";
|
||||
return "FileResource [sha1=" + sha1 + ", artifactId=" + artifactId + ", filename=" + filename + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -168,10 +168,10 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
|
||||
|
||||
private TenantSecurityToken createTenantSecruityTokenVariables(final HttpServletRequest request,
|
||||
final String tenant, final String controllerId) {
|
||||
final TenantSecurityToken secruityToken = new TenantSecurityToken(tenant, controllerId,
|
||||
final TenantSecurityToken secruityToken = new TenantSecurityToken(tenant, null, controllerId, null,
|
||||
FileResource.createFileResourceBySha1(""));
|
||||
final UnmodifiableIterator<String> forEnumeration = Iterators.forEnumeration(request.getHeaderNames());
|
||||
forEnumeration.forEachRemaining(header -> secruityToken.getHeaders().put(header, request.getHeader(header)));
|
||||
forEnumeration.forEachRemaining(header -> secruityToken.putHeader(header, request.getHeader(header)));
|
||||
return secruityToken;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.mgmt.json.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
@@ -72,7 +73,7 @@ public class PagedList<T> extends ResourceSupport {
|
||||
}
|
||||
|
||||
public List<T> getContent() {
|
||||
return content;
|
||||
return Collections.unmodifiableList(content);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,10 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
||||
@@ -34,10 +37,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
/**
|
||||
* A mapper which maps repository model to RESTful model representation and
|
||||
* back.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public final class MgmtDistributionSetMapper {
|
||||
private MgmtDistributionSetMapper() {
|
||||
@@ -75,15 +74,13 @@ public final class MgmtDistributionSetMapper {
|
||||
* to use for conversion
|
||||
* @return converted list of {@link DistributionSet}s
|
||||
*/
|
||||
static List<DistributionSet> dsFromRequest(final Iterable<MgmtDistributionSetRequestBodyPost> sets,
|
||||
static List<DistributionSet> dsFromRequest(final Collection<MgmtDistributionSetRequestBodyPost> sets,
|
||||
final SoftwareManagement softwareManagement, final DistributionSetManagement distributionSetManagement,
|
||||
final EntityFactory entityFactory) {
|
||||
|
||||
final List<DistributionSet> mappedList = new ArrayList<>();
|
||||
for (final MgmtDistributionSetRequestBodyPost dsRest : sets) {
|
||||
mappedList.add(fromRequest(dsRest, softwareManagement, distributionSetManagement, entityFactory));
|
||||
}
|
||||
return mappedList;
|
||||
return sets.stream()
|
||||
.map(dsRest -> fromRequest(dsRest, softwareManagement, distributionSetManagement, entityFactory))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
@@ -139,15 +136,12 @@ public final class MgmtDistributionSetMapper {
|
||||
*/
|
||||
static List<DistributionSetMetadata> fromRequestDsMetadata(final DistributionSet ds,
|
||||
final List<MgmtMetadata> metadata, final EntityFactory entityFactory) {
|
||||
final List<DistributionSetMetadata> mappedList = new ArrayList<>(metadata.size());
|
||||
for (final MgmtMetadata metadataRest : metadata) {
|
||||
if (metadataRest.getKey() == null) {
|
||||
throw new IllegalArgumentException("the key of the metadata must be present");
|
||||
}
|
||||
mappedList.add(
|
||||
entityFactory.generateDistributionSetMetadata(ds, metadataRest.getKey(), metadataRest.getValue()));
|
||||
if (metadata == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappedList;
|
||||
|
||||
return metadata.stream().map(metadataRest -> entityFactory.generateDistributionSetMetadata(ds,
|
||||
metadataRest.getKey(), metadataRest.getValue())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,15 +190,12 @@ public final class MgmtDistributionSetMapper {
|
||||
return result;
|
||||
}
|
||||
|
||||
static List<MgmtDistributionSet> toResponseDistributionSets(final Iterable<DistributionSet> sets) {
|
||||
final List<MgmtDistributionSet> response = new ArrayList<>();
|
||||
if (sets != null) {
|
||||
|
||||
for (final DistributionSet set : sets) {
|
||||
response.add(toResponse(set));
|
||||
}
|
||||
static List<MgmtDistributionSet> toResponseDistributionSets(final Collection<DistributionSet> sets) {
|
||||
if (sets == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return response;
|
||||
|
||||
return sets.stream().map(MgmtDistributionSetMapper::toResponse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static MgmtMetadata toResponseDsMetadata(final DistributionSetMetadata metadata) {
|
||||
@@ -224,14 +215,10 @@ public final class MgmtDistributionSetMapper {
|
||||
}
|
||||
|
||||
static List<MgmtDistributionSet> toResponseFromDsList(final List<DistributionSet> sets) {
|
||||
final List<MgmtDistributionSet> mappedList = new ArrayList<>();
|
||||
if (sets != null) {
|
||||
for (final DistributionSet set : sets) {
|
||||
final MgmtDistributionSet response = toResponse(set);
|
||||
|
||||
mappedList.add(response);
|
||||
}
|
||||
if (sets == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappedList;
|
||||
|
||||
return sets.stream().map(MgmtDistributionSetMapper::toResponse).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -129,7 +130,7 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
|
||||
.runAsSystem(() -> this.systemManagement.getTenantMetadata().getDefaultDsType().getKey());
|
||||
sets.stream().filter(ds -> ds.getType() == null).forEach(ds -> ds.setType(defaultDsKey));
|
||||
|
||||
final Iterable<DistributionSet> createdDSets = this.distributionSetManagement
|
||||
final Collection<DistributionSet> createdDSets = this.distributionSetManagement
|
||||
.createDistributionSets(MgmtDistributionSetMapper.dsFromRequest(sets, this.softwareManagement,
|
||||
this.distributionSetManagement, entityFactory));
|
||||
|
||||
|
||||
@@ -11,8 +11,10 @@ package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetType;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetTypeRequestBodyPost;
|
||||
@@ -39,13 +41,13 @@ final class MgmtDistributionSetTypeMapper {
|
||||
|
||||
static List<DistributionSetType> smFromRequest(final EntityFactory entityFactory,
|
||||
final SoftwareManagement softwareManagement,
|
||||
final Iterable<MgmtDistributionSetTypeRequestBodyPost> smTypesRest) {
|
||||
final List<DistributionSetType> mappedList = new ArrayList<>();
|
||||
|
||||
for (final MgmtDistributionSetTypeRequestBodyPost smRest : smTypesRest) {
|
||||
mappedList.add(fromRequest(entityFactory, softwareManagement, smRest));
|
||||
final Collection<MgmtDistributionSetTypeRequestBodyPost> smTypesRest) {
|
||||
if (smTypesRest == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappedList;
|
||||
|
||||
return smTypesRest.stream().map(smRest -> fromRequest(entityFactory, softwareManagement, smRest))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static DistributionSetType fromRequest(final EntityFactory entityFactory,
|
||||
@@ -91,19 +93,19 @@ final class MgmtDistributionSetTypeMapper {
|
||||
}
|
||||
|
||||
static List<MgmtDistributionSetType> toTypesResponse(final List<DistributionSetType> types) {
|
||||
final List<MgmtDistributionSetType> response = new ArrayList<>();
|
||||
for (final DistributionSetType dsType : types) {
|
||||
response.add(toResponse(dsType));
|
||||
if (types == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return response;
|
||||
|
||||
return types.stream().map(MgmtDistributionSetTypeMapper::toResponse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static List<MgmtDistributionSetType> toListResponse(final List<DistributionSetType> types) {
|
||||
final List<MgmtDistributionSetType> response = new ArrayList<>();
|
||||
for (final DistributionSetType dsType : types) {
|
||||
response.add(toResponse(dsType));
|
||||
if (types == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return response;
|
||||
|
||||
return types.stream().map(MgmtDistributionSetTypeMapper::toResponse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static MgmtDistributionSetType toResponse(final DistributionSetType type) {
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
|
||||
import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtDistributionSetTypeMapper.toResponse;
|
||||
import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtDistributionSetTypeMapper.toTypesResponse;
|
||||
import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleTypeMapper.toResponse;
|
||||
import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleTypeMapper.toTypesResponse;
|
||||
import static org.springframework.http.HttpStatus.CREATED;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
|
||||
@@ -32,7 +38,6 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -42,8 +47,6 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
/**
|
||||
* REST Resource handling for {@link SoftwareModule} and related
|
||||
* {@link Artifact} CRUD operations.
|
||||
*
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeRestApi {
|
||||
@@ -67,7 +70,6 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
|
||||
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
||||
final Sort sorting = PagingUtility.sanitizeDistributionSetTypeSortParam(sortParam);
|
||||
|
||||
final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting);
|
||||
|
||||
final Slice<DistributionSetType> findModuleTypessAll;
|
||||
@@ -82,31 +84,32 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
|
||||
final List<MgmtDistributionSetType> rest = MgmtDistributionSetTypeMapper
|
||||
.toListResponse(findModuleTypessAll.getContent());
|
||||
return new ResponseEntity<>(new PagedList<>(rest, countModulesAll), HttpStatus.OK);
|
||||
return ResponseEntity.ok(new PagedList<>(rest, countModulesAll));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtDistributionSetType> getDistributionSetType(
|
||||
@PathVariable("distributionSetTypeId") final Long distributionSetTypeId) {
|
||||
final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
|
||||
return new ResponseEntity<>(MgmtDistributionSetTypeMapper.toResponse(foundType), HttpStatus.OK);
|
||||
final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
return ResponseEntity.ok(toResponse(foundType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> deleteDistributionSetType(
|
||||
@PathVariable("distributionSetTypeId") final Long distributionSetTypeId) {
|
||||
final DistributionSetType module = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
|
||||
final DistributionSetType module = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
distributionSetManagement.deleteDistributionSetType(module);
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtDistributionSetType> updateDistributionSetType(
|
||||
@PathVariable("distributionSetTypeId") final Long distributionSetTypeId,
|
||||
@RequestBody final MgmtDistributionSetTypeRequestBodyPut restDistributionSetType) {
|
||||
|
||||
final DistributionSetType type = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
|
||||
// only description can be modified
|
||||
@@ -117,8 +120,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
final DistributionSetType updatedDistributionSetType = distributionSetManagement
|
||||
.updateDistributionSetType(type);
|
||||
|
||||
return new ResponseEntity<>(MgmtDistributionSetTypeMapper.toResponse(updatedDistributionSetType),
|
||||
HttpStatus.OK);
|
||||
return ResponseEntity.ok(toResponse(updatedDistributionSetType));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,16 +130,17 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
final List<DistributionSetType> createdSoftwareModules = distributionSetManagement.createDistributionSetTypes(
|
||||
MgmtDistributionSetTypeMapper.smFromRequest(entityFactory, softwareManagement, distributionSetTypes));
|
||||
|
||||
return new ResponseEntity<>(MgmtDistributionSetTypeMapper.toTypesResponse(createdSoftwareModules),
|
||||
HttpStatus.CREATED);
|
||||
return ResponseEntity.status(CREATED).body(toTypesResponse(createdSoftwareModules));
|
||||
}
|
||||
|
||||
private DistributionSetType findDistributionSetTypeWithExceptionIfNotFound(final Long distributionSetTypeId) {
|
||||
|
||||
final DistributionSetType module = distributionSetManagement.findDistributionSetTypeById(distributionSetTypeId);
|
||||
if (module == null) {
|
||||
throw new EntityNotFoundException(
|
||||
"DistributionSetType with Id {" + distributionSetTypeId + "} does not exist");
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
@@ -146,8 +149,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
@PathVariable("distributionSetTypeId") final Long distributionSetTypeId) {
|
||||
|
||||
final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toListResponse(foundType.getMandatoryModuleTypes()),
|
||||
HttpStatus.OK);
|
||||
return ResponseEntity.ok(toTypesResponse(foundType.getMandatoryModuleTypes()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,7 +158,6 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
@PathVariable("softwareModuleTypeId") final Long softwareModuleTypeId) {
|
||||
|
||||
final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
|
||||
final SoftwareModuleType foundSmType = findSoftwareModuleTypeWithExceptionIfNotFound(softwareModuleTypeId);
|
||||
|
||||
if (!foundType.containsMandatoryModuleType(foundSmType)) {
|
||||
@@ -164,7 +165,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
"Software module with given ID is not part of this distribution set type!");
|
||||
}
|
||||
|
||||
return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toResponse(foundSmType), HttpStatus.OK);
|
||||
return ResponseEntity.ok(toResponse(foundSmType));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,7 +174,6 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
@PathVariable("softwareModuleTypeId") final Long softwareModuleTypeId) {
|
||||
|
||||
final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
|
||||
final SoftwareModuleType foundSmType = findSoftwareModuleTypeWithExceptionIfNotFound(softwareModuleTypeId);
|
||||
|
||||
if (!foundType.containsOptionalModuleType(foundSmType)) {
|
||||
@@ -181,7 +181,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
"Software module with given ID is not part of this distribution set type!");
|
||||
}
|
||||
|
||||
return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toResponse(foundSmType), HttpStatus.OK);
|
||||
return ResponseEntity.ok(toResponse(foundSmType));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -189,9 +189,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
@PathVariable("distributionSetTypeId") final Long distributionSetTypeId) {
|
||||
|
||||
final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
|
||||
return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toListResponse(foundType.getOptionalModuleTypes()),
|
||||
HttpStatus.OK);
|
||||
return ResponseEntity.ok(toTypesResponse(foundType.getOptionalModuleTypes()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -200,7 +198,6 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
@PathVariable("softwareModuleTypeId") final Long softwareModuleTypeId) {
|
||||
|
||||
final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
|
||||
final SoftwareModuleType foundSmType = findSoftwareModuleTypeWithExceptionIfNotFound(softwareModuleTypeId);
|
||||
|
||||
if (!foundType.containsMandatoryModuleType(foundSmType)) {
|
||||
@@ -209,18 +206,17 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
}
|
||||
|
||||
foundType.removeModuleType(softwareModuleTypeId);
|
||||
|
||||
distributionSetManagement.updateDistributionSetType(foundType);
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> removeOptionalModule(
|
||||
@PathVariable("distributionSetTypeId") final Long distributionSetTypeId,
|
||||
@PathVariable("softwareModuleTypeId") final Long softwareModuleTypeId) {
|
||||
final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
|
||||
final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
final SoftwareModuleType foundSmType = findSoftwareModuleTypeWithExceptionIfNotFound(softwareModuleTypeId);
|
||||
|
||||
if (!foundType.containsOptionalModuleType(foundSmType)) {
|
||||
@@ -229,10 +225,9 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
}
|
||||
|
||||
foundType.removeModuleType(softwareModuleTypeId);
|
||||
|
||||
distributionSetManagement.updateDistributionSetType(foundType);
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -240,14 +235,11 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
@PathVariable("distributionSetTypeId") final Long distributionSetTypeId, @RequestBody final MgmtId smtId) {
|
||||
|
||||
final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
|
||||
final SoftwareModuleType smType = findSoftwareModuleTypeWithExceptionIfNotFound(smtId.getId());
|
||||
|
||||
foundType.addMandatoryModuleType(smType);
|
||||
|
||||
distributionSetManagement.updateDistributionSetType(foundType);
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -255,23 +247,22 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR
|
||||
@PathVariable("distributionSetTypeId") final Long distributionSetTypeId, @RequestBody final MgmtId smtId) {
|
||||
|
||||
final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId);
|
||||
|
||||
final SoftwareModuleType smType = findSoftwareModuleTypeWithExceptionIfNotFound(smtId.getId());
|
||||
|
||||
foundType.addOptionalModuleType(smType);
|
||||
|
||||
distributionSetManagement.updateDistributionSetType(foundType);
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
private SoftwareModuleType findSoftwareModuleTypeWithExceptionIfNotFound(final Long softwareModuleTypeId) {
|
||||
|
||||
final SoftwareModuleType module = softwareManagement.findSoftwareModuleTypeById(softwareModuleTypeId);
|
||||
if (module == null) {
|
||||
throw new EntityNotFoundException(
|
||||
"SoftwareModuleType with Id {" + softwareModuleTypeId + "} does not exist");
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@ package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutCondition.Condition;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutErrorAction.ErrorAction;
|
||||
@@ -48,9 +49,11 @@ final class MgmtRolloutMapper {
|
||||
}
|
||||
|
||||
static List<MgmtRolloutResponseBody> toResponseRollout(final List<Rollout> rollouts) {
|
||||
final List<MgmtRolloutResponseBody> result = new ArrayList<>(rollouts.size());
|
||||
rollouts.forEach(r -> result.add(toResponseRollout(r)));
|
||||
return result;
|
||||
if (rollouts == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return rollouts.stream().map(MgmtRolloutMapper::toResponseRollout).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static MgmtRolloutResponseBody toResponseRollout(final Rollout rollout) {
|
||||
@@ -103,9 +106,11 @@ final class MgmtRolloutMapper {
|
||||
}
|
||||
|
||||
static List<MgmtRolloutGroupResponseBody> toResponseRolloutGroup(final List<RolloutGroup> rollouts) {
|
||||
final List<MgmtRolloutGroupResponseBody> result = new ArrayList<>(rollouts.size());
|
||||
rollouts.forEach(r -> result.add(toResponseRolloutGroup(r)));
|
||||
return result;
|
||||
if (rollouts == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return rollouts.stream().map(MgmtRolloutMapper::toResponseRolloutGroup).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static MgmtRolloutGroupResponseBody toResponseRolloutGroup(final RolloutGroup rolloutGroup) {
|
||||
|
||||
@@ -11,8 +11,10 @@ package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact;
|
||||
@@ -65,60 +67,46 @@ public final class MgmtSoftwareModuleMapper {
|
||||
}
|
||||
|
||||
static List<SoftwareModuleMetadata> fromRequestSwMetadata(final EntityFactory entityFactory,
|
||||
final SoftwareModule sw, final List<MgmtMetadata> metadata) {
|
||||
final List<SoftwareModuleMetadata> mappedList = new ArrayList<>(metadata.size());
|
||||
for (final MgmtMetadata metadataRest : metadata) {
|
||||
if (metadataRest.getKey() == null) {
|
||||
throw new IllegalArgumentException("the key of the metadata must be present");
|
||||
}
|
||||
mappedList.add(
|
||||
entityFactory.generateSoftwareModuleMetadata(sw, metadataRest.getKey(), metadataRest.getValue()));
|
||||
final SoftwareModule sw, final Collection<MgmtMetadata> metadata) {
|
||||
if (metadata == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappedList;
|
||||
|
||||
return metadata.stream().map(metadataRest -> entityFactory.generateSoftwareModuleMetadata(sw,
|
||||
metadataRest.getKey(), metadataRest.getValue())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static List<SoftwareModule> smFromRequest(final EntityFactory entityFactory,
|
||||
final Iterable<MgmtSoftwareModuleRequestBodyPost> smsRest, final SoftwareManagement softwareManagement) {
|
||||
final List<SoftwareModule> mappedList = new ArrayList<>();
|
||||
for (final MgmtSoftwareModuleRequestBodyPost smRest : smsRest) {
|
||||
mappedList.add(fromRequest(entityFactory, smRest, softwareManagement));
|
||||
final Collection<MgmtSoftwareModuleRequestBodyPost> smsRest, final SoftwareManagement softwareManagement) {
|
||||
if (smsRest == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappedList;
|
||||
|
||||
return smsRest.stream().map(smRest -> fromRequest(entityFactory, smRest, softwareManagement))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create response for sw modules.
|
||||
*
|
||||
* @param baseSoftareModules
|
||||
* @param softwareModules
|
||||
* the modules
|
||||
* @return the response
|
||||
*/
|
||||
public static List<MgmtSoftwareModule> toResponse(final List<SoftwareModule> baseSoftareModules) {
|
||||
final List<MgmtSoftwareModule> mappedList = new ArrayList<>();
|
||||
if (baseSoftareModules != null) {
|
||||
for (final SoftwareModule target : baseSoftareModules) {
|
||||
final MgmtSoftwareModule response = toResponse(target);
|
||||
|
||||
mappedList.add(response);
|
||||
}
|
||||
public static List<MgmtSoftwareModule> toResponse(final Collection<SoftwareModule> softwareModules) {
|
||||
if (softwareModules == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappedList;
|
||||
|
||||
return softwareModules.stream().map(MgmtSoftwareModuleMapper::toResponse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static List<MgmtSoftwareModule> toResponseSoftwareModules(final Iterable<SoftwareModule> softwareModules) {
|
||||
final List<MgmtSoftwareModule> response = new ArrayList<>();
|
||||
for (final SoftwareModule softwareModule : softwareModules) {
|
||||
response.add(toResponse(softwareModule));
|
||||
static List<MgmtMetadata> toResponseSwMetadata(final Collection<SoftwareModuleMetadata> metadata) {
|
||||
if (metadata == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
static List<MgmtMetadata> toResponseSwMetadata(final List<SoftwareModuleMetadata> metadata) {
|
||||
final List<MgmtMetadata> mappedList = new ArrayList<>(metadata.size());
|
||||
for (final SoftwareModuleMetadata distributionSetMetadata : metadata) {
|
||||
mappedList.add(toResponseSwMetadata(distributionSetMetadata));
|
||||
}
|
||||
return mappedList;
|
||||
return metadata.stream().map(MgmtSoftwareModuleMapper::toResponseSwMetadata).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static MgmtMetadata toResponseSwMetadata(final SoftwareModuleMetadata metadata) {
|
||||
@@ -194,15 +182,11 @@ public final class MgmtSoftwareModuleMapper {
|
||||
return artifactRest;
|
||||
}
|
||||
|
||||
static List<MgmtArtifact> artifactsToResponse(final List<Artifact> artifacts) {
|
||||
final List<MgmtArtifact> mappedList = new ArrayList<>();
|
||||
|
||||
if (artifacts != null) {
|
||||
for (final Artifact artifact : artifacts) {
|
||||
final MgmtArtifact response = toResponse(artifact);
|
||||
mappedList.add(response);
|
||||
}
|
||||
static List<MgmtArtifact> artifactsToResponse(final Collection<Artifact> artifacts) {
|
||||
if (artifacts == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappedList;
|
||||
|
||||
return artifacts.stream().map(MgmtSoftwareModuleMapper::toResponse).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,15 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
|
||||
import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleMapper.artifactsToResponse;
|
||||
import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleMapper.toResponse;
|
||||
import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleMapper.toResponseSwMetadata;
|
||||
import static org.springframework.http.HttpStatus.BAD_REQUEST;
|
||||
import static org.springframework.http.HttpStatus.CREATED;
|
||||
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
|
||||
@@ -46,10 +54,10 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
/**
|
||||
* REST Resource handling for {@link SoftwareModule} and related
|
||||
* {@link Artifact} CRUD operations.
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MgmtSoftwareModuleResource.class);
|
||||
|
||||
@Autowired
|
||||
@@ -69,7 +77,7 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
|
||||
@RequestParam(value = "sha1sum", required = false) final String sha1Sum) {
|
||||
|
||||
if (file.isEmpty()) {
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
return new ResponseEntity<>(BAD_REQUEST);
|
||||
}
|
||||
String fileName = optionalFileName;
|
||||
|
||||
@@ -81,10 +89,10 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
|
||||
final Artifact result = artifactManagement.createLocalArtifact(file.getInputStream(), softwareModuleId,
|
||||
fileName, md5Sum == null ? null : md5Sum.toLowerCase(),
|
||||
sha1Sum == null ? null : sha1Sum.toLowerCase(), false, file.getContentType());
|
||||
return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(result), HttpStatus.CREATED);
|
||||
return ResponseEntity.status(CREATED).body(toResponse(result));
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Failed to store artifact", e);
|
||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
return new ResponseEntity<>(INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,31 +100,34 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
|
||||
@ResponseBody
|
||||
public ResponseEntity<List<MgmtArtifact>> getArtifacts(
|
||||
@PathVariable("softwareModuleId") final Long softwareModuleId) {
|
||||
final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
|
||||
|
||||
return new ResponseEntity<>(MgmtSoftwareModuleMapper.artifactsToResponse(module.getArtifacts()), HttpStatus.OK);
|
||||
final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
|
||||
return ResponseEntity.ok(artifactsToResponse(module.getArtifacts()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ResponseBody
|
||||
// Exception squid:S3655 - Optional access is checked in
|
||||
// findSoftwareModuleWithExceptionIfNotFound
|
||||
// subroutine
|
||||
@SuppressWarnings("squid:S3655")
|
||||
public ResponseEntity<MgmtArtifact> getArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||
@PathVariable("artifactId") final Long artifactId) {
|
||||
|
||||
final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId);
|
||||
|
||||
return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(module.getLocalArtifact(artifactId).get()),
|
||||
HttpStatus.OK);
|
||||
return ResponseEntity.ok(toResponse(module.getLocalArtifact(artifactId).get()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ResponseBody
|
||||
public ResponseEntity<Void> deleteArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||
@PathVariable("artifactId") final Long artifactId) {
|
||||
findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId);
|
||||
|
||||
findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId);
|
||||
artifactManagement.deleteLocalArtifact(artifactId);
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -143,33 +154,34 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
|
||||
}
|
||||
|
||||
final List<MgmtSoftwareModule> rest = MgmtSoftwareModuleMapper.toResponse(findModulesAll.getContent());
|
||||
return new ResponseEntity<>(new PagedList<>(rest, countModulesAll), HttpStatus.OK);
|
||||
return ResponseEntity.ok(new PagedList<>(rest, countModulesAll));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtSoftwareModule> getSoftwareModule(
|
||||
@PathVariable("softwareModuleId") final Long softwareModuleId) {
|
||||
final SoftwareModule findBaseSoftareModule = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
|
||||
|
||||
return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(findBaseSoftareModule), HttpStatus.OK);
|
||||
final SoftwareModule findBaseSoftareModule = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
|
||||
return ResponseEntity.ok(toResponse(findBaseSoftareModule));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<List<MgmtSoftwareModule>> createSoftwareModules(
|
||||
@RequestBody final List<MgmtSoftwareModuleRequestBodyPost> softwareModules) {
|
||||
|
||||
LOG.debug("creating {} softwareModules", softwareModules.size());
|
||||
final Iterable<SoftwareModule> createdSoftwareModules = softwareManagement.createSoftwareModule(
|
||||
final Collection<SoftwareModule> createdSoftwareModules = softwareManagement.createSoftwareModule(
|
||||
MgmtSoftwareModuleMapper.smFromRequest(entityFactory, softwareModules, softwareManagement));
|
||||
LOG.debug("{} softwareModules created, return status {}", softwareModules.size(), HttpStatus.CREATED);
|
||||
|
||||
return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponseSoftwareModules(createdSoftwareModules),
|
||||
HttpStatus.CREATED);
|
||||
return ResponseEntity.status(CREATED).body(toResponse(createdSoftwareModules));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtSoftwareModule> updateSoftwareModule(
|
||||
@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||
@RequestBody final MgmtSoftwareModuleRequestBodyPut restSoftwareModule) {
|
||||
|
||||
final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
|
||||
|
||||
// only description and vendor can be modified
|
||||
@@ -181,16 +193,16 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
|
||||
}
|
||||
|
||||
final SoftwareModule updateSoftwareModule = softwareManagement.updateSoftwareModule(module);
|
||||
return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(updateSoftwareModule), HttpStatus.OK);
|
||||
return ResponseEntity.ok(toResponse(updateSoftwareModule));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> deleteSoftwareModule(@PathVariable("softwareModuleId") final Long softwareModuleId) {
|
||||
final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
|
||||
|
||||
final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
|
||||
softwareManagement.deleteSoftwareModule(module);
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -218,34 +230,38 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
|
||||
metaDataPage = softwareManagement.findSoftwareModuleMetadataBySoftwareModuleId(softwareModuleId, pageable);
|
||||
}
|
||||
|
||||
return new ResponseEntity<>(
|
||||
new PagedList<>(MgmtSoftwareModuleMapper.toResponseSwMetadata(metaDataPage.getContent()),
|
||||
metaDataPage.getTotalElements()),
|
||||
HttpStatus.OK);
|
||||
return ResponseEntity
|
||||
.ok(new PagedList<>(toResponseSwMetadata(metaDataPage.getContent()), metaDataPage.getTotalElements()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtMetadata> getMetadataValue(@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||
@PathVariable("metadataKey") final String metadataKey) {
|
||||
|
||||
final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
|
||||
final SoftwareModuleMetadata findOne = softwareManagement.findSoftwareModuleMetadata(sw, metadataKey);
|
||||
return ResponseEntity.<MgmtMetadata> ok(MgmtSoftwareModuleMapper.toResponseSwMetadata(findOne));
|
||||
|
||||
return ResponseEntity.ok(toResponseSwMetadata(findOne));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtMetadata> updateMetadata(@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||
@PathVariable("metadataKey") final String metadataKey, @RequestBody final MgmtMetadata metadata) {
|
||||
|
||||
final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
|
||||
final SoftwareModuleMetadata updated = softwareManagement.updateSoftwareModuleMetadata(
|
||||
entityFactory.generateSoftwareModuleMetadata(sw, metadataKey, metadata.getValue()));
|
||||
return ResponseEntity.ok(MgmtSoftwareModuleMapper.toResponseSwMetadata(updated));
|
||||
|
||||
return ResponseEntity.ok(toResponseSwMetadata(updated));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> deleteMetadata(@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||
@PathVariable("metadataKey") final String metadataKey) {
|
||||
|
||||
final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
|
||||
softwareManagement.deleteSoftwareModuleMetadata(sw, metadataKey);
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@@ -253,24 +269,25 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
|
||||
public ResponseEntity<List<MgmtMetadata>> createMetadata(
|
||||
@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||
@RequestBody final List<MgmtMetadata> metadataRest) {
|
||||
final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
|
||||
|
||||
final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null);
|
||||
final List<SoftwareModuleMetadata> created = softwareManagement.createSoftwareModuleMetadata(
|
||||
MgmtSoftwareModuleMapper.fromRequestSwMetadata(entityFactory, sw, metadataRest));
|
||||
|
||||
return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponseSwMetadata(created), HttpStatus.CREATED);
|
||||
|
||||
return ResponseEntity.status(CREATED).body(toResponseSwMetadata(created));
|
||||
}
|
||||
|
||||
private SoftwareModule findSoftwareModuleWithExceptionIfNotFound(final Long softwareModuleId,
|
||||
final Long artifactId) {
|
||||
|
||||
final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId);
|
||||
if (module == null) {
|
||||
throw new EntityNotFoundException("SoftwareModule with Id {" + softwareModuleId + "} does not exist");
|
||||
} else if (artifactId != null && !module.getLocalArtifact(artifactId).isPresent()) {
|
||||
}
|
||||
if (artifactId != null && !module.getLocalArtifact(artifactId).isPresent()) {
|
||||
throw new EntityNotFoundException("Artifact with Id {" + artifactId + "} does not exist");
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@ package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeRequestBodyPost;
|
||||
@@ -25,9 +26,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
* A mapper which maps repository model to RESTful model representation and
|
||||
* back.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
final class MgmtSoftwareModuleTypeMapper {
|
||||
|
||||
@@ -37,13 +35,12 @@ final class MgmtSoftwareModuleTypeMapper {
|
||||
}
|
||||
|
||||
static List<SoftwareModuleType> smFromRequest(final EntityFactory entityFactory,
|
||||
final Iterable<MgmtSoftwareModuleTypeRequestBodyPost> smTypesRest) {
|
||||
final List<SoftwareModuleType> mappedList = new ArrayList<>();
|
||||
|
||||
for (final MgmtSoftwareModuleTypeRequestBodyPost smRest : smTypesRest) {
|
||||
mappedList.add(fromRequest(entityFactory, smRest));
|
||||
final Collection<MgmtSoftwareModuleTypeRequestBodyPost> smTypesRest) {
|
||||
if (smTypesRest == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappedList;
|
||||
|
||||
return smTypesRest.stream().map(smRest -> fromRequest(entityFactory, smRest)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static SoftwareModuleType fromRequest(final EntityFactory entityFactory,
|
||||
@@ -57,20 +54,12 @@ final class MgmtSoftwareModuleTypeMapper {
|
||||
return result;
|
||||
}
|
||||
|
||||
static List<MgmtSoftwareModuleType> toTypesResponse(final List<SoftwareModuleType> types) {
|
||||
final List<MgmtSoftwareModuleType> response = new ArrayList<>();
|
||||
for (final SoftwareModuleType softwareModule : types) {
|
||||
response.add(toResponse(softwareModule));
|
||||
static List<MgmtSoftwareModuleType> toTypesResponse(final Collection<SoftwareModuleType> types) {
|
||||
if (types == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
static List<MgmtSoftwareModuleType> toListResponse(final Collection<SoftwareModuleType> types) {
|
||||
final List<MgmtSoftwareModuleType> response = new ArrayList<>();
|
||||
for (final SoftwareModuleType softwareModule : types) {
|
||||
response.add(toResponse(softwareModule));
|
||||
}
|
||||
return response;
|
||||
return types.stream().map(MgmtSoftwareModuleTypeMapper::toResponse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static MgmtSoftwareModuleType toResponse(final SoftwareModuleType type) {
|
||||
|
||||
@@ -72,7 +72,7 @@ public class MgmtSoftwareModuleTypeResource implements MgmtSoftwareModuleTypeRes
|
||||
}
|
||||
|
||||
final List<MgmtSoftwareModuleType> rest = MgmtSoftwareModuleTypeMapper
|
||||
.toListResponse(findModuleTypessAll.getContent());
|
||||
.toTypesResponse(findModuleTypessAll.getContent());
|
||||
return new ResponseEntity<>(new PagedList<>(rest, countModulesAll), HttpStatus.OK);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,11 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtPollStatus;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
|
||||
@@ -92,17 +94,17 @@ public final class MgmtTargetMapper {
|
||||
* the targets
|
||||
* @return the response
|
||||
*/
|
||||
public static List<MgmtTarget> toResponseWithLinksAndPollStatus(final Iterable<Target> targets) {
|
||||
final List<MgmtTarget> mappedList = new ArrayList<>();
|
||||
if (targets != null) {
|
||||
for (final Target target : targets) {
|
||||
final MgmtTarget response = toResponse(target);
|
||||
addPollStatus(target, response);
|
||||
addTargetLinks(response);
|
||||
mappedList.add(response);
|
||||
}
|
||||
public static List<MgmtTarget> toResponseWithLinksAndPollStatus(final Collection<Target> targets) {
|
||||
if (targets == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappedList;
|
||||
|
||||
return targets.stream().map(target -> {
|
||||
final MgmtTarget response = toResponse(target);
|
||||
addPollStatus(target, response);
|
||||
addTargetLinks(response);
|
||||
return response;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,15 +114,12 @@ public final class MgmtTargetMapper {
|
||||
* list of targets
|
||||
* @return the response
|
||||
*/
|
||||
public static List<MgmtTarget> toResponse(final Iterable<Target> targets) {
|
||||
final List<MgmtTarget> mappedList = new ArrayList<>();
|
||||
if (targets != null) {
|
||||
for (final Target target : targets) {
|
||||
final MgmtTarget response = toResponse(target);
|
||||
mappedList.add(response);
|
||||
}
|
||||
public static List<MgmtTarget> toResponse(final Collection<Target> targets) {
|
||||
if (targets == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappedList;
|
||||
|
||||
return targets.stream().map(MgmtTargetMapper::toResponse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,12 +172,13 @@ public final class MgmtTargetMapper {
|
||||
}
|
||||
|
||||
static List<Target> fromRequest(final EntityFactory entityFactory,
|
||||
final Iterable<MgmtTargetRequestBody> targetsRest) {
|
||||
final List<Target> mappedList = new ArrayList<>();
|
||||
for (final MgmtTargetRequestBody targetRest : targetsRest) {
|
||||
mappedList.add(fromRequest(entityFactory, targetRest));
|
||||
final Collection<MgmtTargetRequestBody> targetsRest) {
|
||||
if (targetsRest == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappedList;
|
||||
|
||||
return targetsRest.stream().map(targetRest -> fromRequest(entityFactory, targetRest))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static Target fromRequest(final EntityFactory entityFactory, final MgmtTargetRequestBody targetRest) {
|
||||
@@ -190,17 +190,12 @@ public final class MgmtTargetMapper {
|
||||
return target;
|
||||
}
|
||||
|
||||
static List<MgmtActionStatus> toActionStatusRestResponse(final List<ActionStatus> actionStatus) {
|
||||
final List<MgmtActionStatus> mappedList = new ArrayList<>();
|
||||
|
||||
if (actionStatus != null) {
|
||||
for (final ActionStatus status : actionStatus) {
|
||||
final MgmtActionStatus response = toResponse(status);
|
||||
mappedList.add(response);
|
||||
}
|
||||
static List<MgmtActionStatus> toActionStatusRestResponse(final Collection<ActionStatus> actionStatus) {
|
||||
if (actionStatus == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return mappedList;
|
||||
return actionStatus.stream().map(MgmtTargetMapper::toResponse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static MgmtAction toResponse(final String targetId, final Action action, final boolean isActive) {
|
||||
@@ -222,14 +217,13 @@ public final class MgmtTargetMapper {
|
||||
return result;
|
||||
}
|
||||
|
||||
static List<MgmtAction> toResponse(final String targetId, final List<Action> actions) {
|
||||
final List<MgmtAction> mappedList = new ArrayList<>();
|
||||
|
||||
for (final Action action : actions) {
|
||||
final MgmtAction response = toResponse(targetId, action, action.isActive());
|
||||
mappedList.add(response);
|
||||
static List<MgmtAction> toResponse(final String targetId, final Collection<Action> actions) {
|
||||
if (actions == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappedList;
|
||||
|
||||
return actions.stream().map(action -> toResponse(targetId, action, action.isActive()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static String getNameOfActionStatusType(final Action.Status type) {
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -108,7 +109,7 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
@Override
|
||||
public ResponseEntity<List<MgmtTarget>> createTargets(@RequestBody final List<MgmtTargetRequestBody> targets) {
|
||||
LOG.debug("creating {} targets", targets.size());
|
||||
final Iterable<Target> createdTargets = this.targetManagement
|
||||
final Collection<Target> createdTargets = this.targetManagement
|
||||
.createTargets(MgmtTargetMapper.fromRequest(entityFactory, targets));
|
||||
LOG.debug("{} targets created, return status {}", targets.size(), HttpStatus.CREATED);
|
||||
return new ResponseEntity<>(MgmtTargetMapper.toResponse(createdTargets), HttpStatus.CREATED);
|
||||
|
||||
@@ -494,9 +494,6 @@ public class MgmtDistributionSetTypeResourceTest extends AbstractRestIntegration
|
||||
final SoftwareModuleType testSmType = softwareManagement.createSoftwareModuleType(
|
||||
entityFactory.generateSoftwareModuleType("test123", "TestName123", "Desc123", 5));
|
||||
|
||||
final List<DistributionSetType> types = new ArrayList<>();
|
||||
types.add(testType);
|
||||
|
||||
// DST does not exist
|
||||
mvc.perform(get("/rest/v1/distributionsettypes/12345678")).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
@@ -554,9 +551,8 @@ public class MgmtDistributionSetTypeResourceTest extends AbstractRestIntegration
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
final DistributionSetType missingName = entityFactory.generateDistributionSetType("test123", null, "Desc123");
|
||||
mvc.perform(post("/rest/v1/distributionsettypes")
|
||||
.content(JsonBuilder.distributionSetTypes(Lists.newArrayList(missingName)))
|
||||
// Missing mandatory field name
|
||||
mvc.perform(post("/rest/v1/distributionsettypes").content("[{\"description\":\"Desc123\",\"key\":\"test123\"}]")
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
|
||||
@@ -438,8 +438,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTestW
|
||||
SoftwareModule sm = entityFactory.generateSoftwareModule(osType, "name 1", "version 1", null, null);
|
||||
sm = softwareManagement.createSoftwareModule(sm);
|
||||
|
||||
final List<SoftwareModule> modules = new ArrayList<>();
|
||||
modules.add(sm);
|
||||
final List<SoftwareModule> modules = Lists.newArrayList(sm);
|
||||
|
||||
// SM does not exist
|
||||
mvc.perform(get("/rest/v1/softwaremodules/12345678")).andDo(MockMvcResultPrinter.print())
|
||||
@@ -457,11 +456,10 @@ public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTestW
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
final SoftwareModule missingName = entityFactory.generateSoftwareModule(osType, null, "version 1", null, null);
|
||||
mvc.perform(
|
||||
post("/rest/v1/softwaremodules").content(JsonBuilder.softwareModules(Lists.newArrayList(missingName)))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
|
||||
mvc.perform(post("/rest/v1/softwaremodules")
|
||||
.content("[{\"description\":\"Desc123\",\"key\":\"test123\", \"type\":\"os\"}]")
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
final SoftwareModule toLongName = entityFactory.generateSoftwareModule(osType,
|
||||
RandomStringUtils.randomAscii(80), "version 1", null, null);
|
||||
|
||||
@@ -318,8 +318,7 @@ public class MgmtSoftwareModuleTypeResourceTest extends AbstractRestIntegrationT
|
||||
final SoftwareModuleType testType = softwareManagement.createSoftwareModuleType(
|
||||
entityFactory.generateSoftwareModuleType("test123", "TestName123", "Desc123", 5));
|
||||
|
||||
final List<SoftwareModuleType> types = new ArrayList<>();
|
||||
types.add(testType);
|
||||
final List<SoftwareModuleType> types = Lists.newArrayList(testType);
|
||||
|
||||
// SM does not exist
|
||||
mvc.perform(get("/rest/v1/softwaremoduletypes/12345678")).andDo(MockMvcResultPrinter.print())
|
||||
@@ -337,9 +336,9 @@ public class MgmtSoftwareModuleTypeResourceTest extends AbstractRestIntegrationT
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
final SoftwareModuleType missingName = entityFactory.generateSoftwareModuleType("test123", null, "Desc123", 5);
|
||||
mvc.perform(post("/rest/v1/softwaremoduletypes")
|
||||
.content(JsonBuilder.softwareModuleTypes(Lists.newArrayList(missingName)))
|
||||
.content(
|
||||
"[{\"description\":\"Desc123\",\"id\":9223372036854775807,\"key\":\"test123\",\"maxAssignments\":5}]")
|
||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" />
|
||||
|
||||
|
||||
<!-- <Logger name="org.eclipse.hawkbit.rest.util.MockMvcResultPrinter" level="DEBUG" /> -->
|
||||
<Logger name="org.eclipse.hawkbit.rest.util.MockMvcResultPrinter" level="DEBUG" />
|
||||
|
||||
<!-- Security Log with hints on potential attacks -->
|
||||
<logger name="server-security" level="INFO" />
|
||||
|
||||
@@ -208,15 +208,16 @@ public interface ArtifactManagement {
|
||||
void deleteLocalArtifact(@NotNull Long id);
|
||||
|
||||
/**
|
||||
* Searches for {@link Artifact} with given {@link Identifiable}.
|
||||
* Searches for {@link LocalArtifact} with given {@link Identifiable}.
|
||||
*
|
||||
* @param id
|
||||
* to search for
|
||||
* @return found {@link Artifact} or <code>null</code> is it could not be
|
||||
* found.
|
||||
* @return found {@link LocalArtifact} or <code>null</code> is it could not
|
||||
* be found.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
Artifact findArtifact(@NotNull Long id);
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_CONTROLLER)
|
||||
LocalArtifact findLocalArtifact(@NotNull Long id);
|
||||
|
||||
/**
|
||||
* Find by artifact by software module id and filename.
|
||||
|
||||
@@ -183,22 +183,6 @@ public interface ControllerManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
String getPollingTime();
|
||||
|
||||
/**
|
||||
* An direct access to the security token of an
|
||||
* {@link Target#getSecurityToken()} without authorization. This is
|
||||
* necessary to be able to access the security-token without any
|
||||
* security-context information because the security-token is used for
|
||||
* authentication.
|
||||
*
|
||||
* @param controllerId
|
||||
* the ID of the controller to retrieve the security token for
|
||||
* @return the security context of the target, in case no target exists for
|
||||
* the given controllerId {@code null} is returned
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.HAS_AUTH_READ_TARGET_SEC_TOKEN)
|
||||
String getSecurityTokenByControllerId(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
* Checks if a given target has currently or has even been assigned to the
|
||||
* given artifact through the action history list. This can e.g. indicate if
|
||||
@@ -218,6 +202,25 @@ public interface ControllerManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
boolean hasTargetArtifactAssigned(@NotNull String controllerId, @NotNull LocalArtifact localArtifact);
|
||||
|
||||
/**
|
||||
* Checks if a given target has currently or has even been assigned to the
|
||||
* given artifact through the action history list. This can e.g. indicate if
|
||||
* a target is allowed to download a given artifact because it has currently
|
||||
* assigned or had ever been assigned to the target and so it's visible to a
|
||||
* specific target e.g. for downloading.
|
||||
*
|
||||
* @param targetId
|
||||
* the ID of the target to check
|
||||
* @param localArtifact
|
||||
* the artifact to verify if the given target had even been
|
||||
* assigned to
|
||||
* @return {@code true} if the given target has currently or had ever a
|
||||
* relation to the given artifact through the action history,
|
||||
* otherwise {@code false}
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
boolean hasTargetArtifactAssigned(@NotNull Long targetId, @NotNull LocalArtifact localArtifact);
|
||||
|
||||
/**
|
||||
* Registers retrieved status for given {@link Target} and {@link Action} if
|
||||
* it does not exist yet.
|
||||
@@ -300,4 +303,32 @@ public interface ControllerManagement {
|
||||
TargetInfo updateTargetStatus(@NotNull TargetInfo targetInfo, TargetUpdateStatus status, Long lastTargetQuery,
|
||||
URI address);
|
||||
|
||||
/**
|
||||
* Finds {@link Target} based on given controller ID returns found Target
|
||||
* without details, i.e. NO {@link Target#getTags()} and
|
||||
* {@link Target#getActions()} possible.
|
||||
*
|
||||
* @param controllerId
|
||||
* to look for.
|
||||
* @return {@link Target} or {@code null} if it does not exist
|
||||
* @see Target#getControllerId()
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
Target findByControllerId(@NotEmpty final String controllerId);
|
||||
|
||||
/**
|
||||
* Finds {@link Target} based on given ID returns found Target without
|
||||
* details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()}
|
||||
* possible.
|
||||
*
|
||||
* @param targetId
|
||||
* to look for.
|
||||
* @return {@link Target} or {@code null} if it does not exist
|
||||
* @see Target#getId()
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
Target findByTargetId(final long targetId);
|
||||
|
||||
}
|
||||
|
||||
@@ -57,7 +57,11 @@ public class DistributionSetAssignmentResult extends AssignmentResult<Target> {
|
||||
* @return the actionIds
|
||||
*/
|
||||
public List<Long> getActions() {
|
||||
return actions;
|
||||
if (actions == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(actions);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -205,8 +205,10 @@ public interface DistributionSetManagement {
|
||||
/**
|
||||
* deletes a distribution set meta data entry.
|
||||
*
|
||||
* @param id
|
||||
* the ID of the distribution set meta data to delete
|
||||
* @param distributionSet
|
||||
* where meta data has to be deleted
|
||||
* @param key
|
||||
* of the meta data element
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
void deleteDistributionSetMetadata(@NotNull final DistributionSet distributionSet, @NotNull final String key);
|
||||
@@ -429,7 +431,7 @@ public interface DistributionSetManagement {
|
||||
*/
|
||||
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
DistributionSetType findDistributionSetTypeByKey(@NotNull String key);
|
||||
DistributionSetType findDistributionSetTypeByKey(@NotEmpty String key);
|
||||
|
||||
/**
|
||||
* @param name
|
||||
@@ -469,15 +471,16 @@ public interface DistributionSetManagement {
|
||||
/**
|
||||
* finds a single distribution set meta data by its id.
|
||||
*
|
||||
* @param id
|
||||
* the id of the distribution set meta data containing the meta
|
||||
* data key and the ID of the distribution set
|
||||
* @param distributionSet
|
||||
* where meta data has to rind
|
||||
* @param key
|
||||
* of the meta data element
|
||||
* @return the found DistributionSetMetadata or {@code null} if not exits
|
||||
* @throws EntityNotFoundException
|
||||
* in case the meta data does not exists for the given key
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
DistributionSetMetadata findOne(@NotNull DistributionSet distributionSet, @NotNull String key);
|
||||
DistributionSetMetadata findOne(@NotNull DistributionSet distributionSet, @NotEmpty String key);
|
||||
|
||||
/**
|
||||
* Checks if a {@link DistributionSet} is currently in use by a target in
|
||||
|
||||
@@ -11,6 +11,8 @@ package org.eclipse.hawkbit.repository;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
@@ -64,7 +66,7 @@ public interface EntityFactory {
|
||||
*
|
||||
* @return {@link ActionStatus} object
|
||||
*/
|
||||
ActionStatus generateActionStatus(Action action, Status status, Long occurredAt);
|
||||
ActionStatus generateActionStatus(@NotNull Action action, @NotNull Status status, Long occurredAt);
|
||||
|
||||
/**
|
||||
* Generates an {@link ActionStatus} object without persisting it.
|
||||
@@ -81,7 +83,7 @@ public interface EntityFactory {
|
||||
*
|
||||
* @return {@link ActionStatus} object
|
||||
*/
|
||||
ActionStatus generateActionStatus(Action action, final Status status, Long occurredAt,
|
||||
ActionStatus generateActionStatus(@NotNull Action action, @NotNull Status status, Long occurredAt,
|
||||
final Collection<String> messages);
|
||||
|
||||
/**
|
||||
@@ -99,7 +101,8 @@ public interface EntityFactory {
|
||||
*
|
||||
* @return {@link ActionStatus} object
|
||||
*/
|
||||
ActionStatus generateActionStatus(Action action, Status status, Long occurredAt, final String message);
|
||||
ActionStatus generateActionStatus(@NotNull Action action, @NotNull Status status, Long occurredAt,
|
||||
final String message);
|
||||
|
||||
/**
|
||||
* Generates an empty {@link DistributionSet} without persisting it.
|
||||
@@ -124,8 +127,8 @@ public interface EntityFactory {
|
||||
*
|
||||
* @return {@link DistributionSet} object
|
||||
*/
|
||||
DistributionSet generateDistributionSet(String name, String version, String description, DistributionSetType type,
|
||||
Collection<SoftwareModule> moduleList);
|
||||
DistributionSet generateDistributionSet(@NotNull String name, @NotNull String version, String description,
|
||||
@NotNull DistributionSetType type, Collection<SoftwareModule> moduleList);
|
||||
|
||||
/**
|
||||
* Generates an empty {@link DistributionSetMetadata} element without
|
||||
@@ -148,7 +151,8 @@ public interface EntityFactory {
|
||||
*
|
||||
* @return {@link DistributionSetMetadata} object
|
||||
*/
|
||||
DistributionSetMetadata generateDistributionSetMetadata(DistributionSet distributionSet, String key, String value);
|
||||
DistributionSetMetadata generateDistributionSetMetadata(@NotNull DistributionSet distributionSet,
|
||||
@NotNull String key, String value);
|
||||
|
||||
/**
|
||||
* Generates an empty {@link DistributionSetTag} without persisting it.
|
||||
@@ -164,7 +168,7 @@ public interface EntityFactory {
|
||||
* of the tag
|
||||
* @return {@link DistributionSetTag} object
|
||||
*/
|
||||
DistributionSetTag generateDistributionSetTag(String name);
|
||||
DistributionSetTag generateDistributionSetTag(@NotNull String name);
|
||||
|
||||
/**
|
||||
* Generates a {@link DistributionSetTag} without persisting it.
|
||||
@@ -177,7 +181,7 @@ public interface EntityFactory {
|
||||
* of the tag
|
||||
* @return {@link DistributionSetTag} object
|
||||
*/
|
||||
DistributionSetTag generateDistributionSetTag(String name, String description, String colour);
|
||||
DistributionSetTag generateDistributionSetTag(@NotNull String name, String description, String colour);
|
||||
|
||||
/**
|
||||
* Generates an empty {@link DistributionSetType} without persisting it.
|
||||
@@ -198,7 +202,7 @@ public interface EntityFactory {
|
||||
*
|
||||
* @return {@link DistributionSetType} object
|
||||
*/
|
||||
DistributionSetType generateDistributionSetType(String key, String name, String description);
|
||||
DistributionSetType generateDistributionSetType(@NotNull String key, @NotNull String name, String description);
|
||||
|
||||
/**
|
||||
* Generates an empty {@link Rollout} without persisting it.
|
||||
@@ -237,8 +241,8 @@ public interface EntityFactory {
|
||||
*
|
||||
* @return {@link SoftwareModule} object
|
||||
*/
|
||||
SoftwareModule generateSoftwareModule(SoftwareModuleType type, String name, String version, String description,
|
||||
String vendor);
|
||||
SoftwareModule generateSoftwareModule(@NotNull SoftwareModuleType type, @NotNull String name,
|
||||
@NotNull String version, String description, String vendor);
|
||||
|
||||
/**
|
||||
* Generates an empty {@link SoftwareModuleMetadata} pair without persisting
|
||||
@@ -260,7 +264,8 @@ public interface EntityFactory {
|
||||
*
|
||||
* @return {@link SoftwareModuleMetadata} object
|
||||
*/
|
||||
SoftwareModuleMetadata generateSoftwareModuleMetadata(SoftwareModule softwareModule, String key, String value);
|
||||
SoftwareModuleMetadata generateSoftwareModuleMetadata(@NotNull SoftwareModule softwareModule, @NotNull String key,
|
||||
String value);
|
||||
|
||||
/**
|
||||
* Generates an empty {@link SoftwareModuleType} without persisting it.
|
||||
@@ -283,7 +288,8 @@ public interface EntityFactory {
|
||||
*
|
||||
* @return {@link SoftwareModuleType} object
|
||||
*/
|
||||
SoftwareModuleType generateSoftwareModuleType(String key, String name, String description, int maxAssignments);
|
||||
SoftwareModuleType generateSoftwareModuleType(@NotNull String key, @NotNull String name, String description,
|
||||
int maxAssignments);
|
||||
|
||||
/**
|
||||
* Generates an empty {@link Target} without persisting it.
|
||||
@@ -307,7 +313,7 @@ public interface EntityFactory {
|
||||
*
|
||||
* @return {@link Target} object
|
||||
*/
|
||||
Target generateTarget(@NotEmpty String controllerID, @NotEmpty String securityToken);
|
||||
Target generateTarget(@NotEmpty String controllerID, String securityToken);
|
||||
|
||||
/**
|
||||
* Generates an empty {@link TargetFilterQuery} without persisting it.
|
||||
@@ -354,7 +360,7 @@ public interface EntityFactory {
|
||||
* of the tag
|
||||
* @return {@link TargetTag} object
|
||||
*/
|
||||
TargetTag generateTargetTag(String name);
|
||||
TargetTag generateTargetTag(@NotNull String name);
|
||||
|
||||
/**
|
||||
* Generates a {@link TargetTag} without persisting it.
|
||||
@@ -367,7 +373,7 @@ public interface EntityFactory {
|
||||
* of the tag
|
||||
* @return {@link TargetTag} object
|
||||
*/
|
||||
TargetTag generateTargetTag(String name, String description, String colour);
|
||||
TargetTag generateTargetTag(@NotNull String name, String description, String colour);
|
||||
|
||||
/**
|
||||
* Generates an empty {@link LocalArtifact} without persisting it.
|
||||
|
||||
@@ -155,11 +155,13 @@ public interface SoftwareManagement {
|
||||
/**
|
||||
* deletes a software module meta data entry.
|
||||
*
|
||||
* @param id
|
||||
* the ID of the software module meta data to delete
|
||||
* @param softwareModule
|
||||
* where meta data has to be deleted
|
||||
* @param key
|
||||
* of the metda data element
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
void deleteSoftwareModuleMetadata(@NotNull SoftwareModule softwareModule, @NotNull String key);
|
||||
void deleteSoftwareModuleMetadata(@NotNull SoftwareModule softwareModule, @NotEmpty String key);
|
||||
|
||||
/**
|
||||
* Deletes {@link SoftwareModule}s which is any if the given ids.
|
||||
@@ -251,9 +253,10 @@ public interface SoftwareManagement {
|
||||
/**
|
||||
* finds a single software module meta data by its id.
|
||||
*
|
||||
* @param id
|
||||
* the id of the software module meta data containing the meta
|
||||
* data key and the ID of the software module
|
||||
* @param softwareModule
|
||||
* where meta data has to be found
|
||||
* @param key
|
||||
* of the meta data element
|
||||
* @return the found SoftwareModuleMetadata or {@code null} if not exits
|
||||
* @throws EntityNotFoundException
|
||||
* in case the meta data does not exists for the given key
|
||||
@@ -280,8 +283,8 @@ public interface SoftwareManagement {
|
||||
*
|
||||
* @param softwareModuleId
|
||||
* the software module id to retrieve the meta data from
|
||||
* @param spec
|
||||
* the specification to filter the result
|
||||
* @param rsqlParam
|
||||
* filter definition in RSQL syntax
|
||||
* @param pageable
|
||||
* the page request to page the result
|
||||
* @return a paged result of all meta data entries for a given software
|
||||
@@ -346,8 +349,8 @@ public interface SoftwareManagement {
|
||||
/**
|
||||
* Retrieves all {@link SoftwareModule}s with a given specification.
|
||||
*
|
||||
* @param spec
|
||||
* the specification to filter the software modules
|
||||
* @param rsqlParam
|
||||
* filter definition in RSQL syntax
|
||||
* @param pageable
|
||||
* pagination parameter
|
||||
* @return the found {@link SoftwareModule}s
|
||||
@@ -392,7 +395,7 @@ public interface SoftwareManagement {
|
||||
* {@link SoftwareModuleType#getKey()}
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
SoftwareModuleType findSoftwareModuleTypeByKey(@NotNull String key);
|
||||
SoftwareModuleType findSoftwareModuleTypeByKey(@NotEmpty String key);
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -415,8 +418,8 @@ public interface SoftwareManagement {
|
||||
/**
|
||||
* Retrieves all {@link SoftwareModuleType}s with a given specification.
|
||||
*
|
||||
* @param spec
|
||||
* the specification to filter the software modules types
|
||||
* @param rsqlParam
|
||||
* filter definition in RSQL syntax
|
||||
* @param pageable
|
||||
* pagination parameter
|
||||
* @return the found {@link SoftwareModuleType}s
|
||||
|
||||
@@ -63,7 +63,8 @@ public interface SystemManagement {
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
|
||||
+ SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_CONTROLLER)
|
||||
TenantMetaData getTenantMetadata();
|
||||
|
||||
/**
|
||||
@@ -93,4 +94,14 @@ public interface SystemManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
|
||||
TenantMetaData updateTenantMetadata(@NotNull TenantMetaData metaData);
|
||||
|
||||
/**
|
||||
* Returns {@link TenantMetaData} of given tenant ID.
|
||||
*
|
||||
* @param tenantId
|
||||
* to retrieve data for
|
||||
* @return {@link TenantMetaData} of given tenant
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
TenantMetaData getTenantMetadata(@NotNull Long tenantId);
|
||||
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ public class RolloutGroupCreatedEvent extends AbstractDistributedEvent {
|
||||
* the revision of the event
|
||||
* @param rolloutId
|
||||
* the ID of the rollout the group has been created
|
||||
* @param rolloutGroupId
|
||||
* identifier of this group
|
||||
* @param totalRolloutGroup
|
||||
* the total number of rollout groups for this rollout
|
||||
* @param createdRolloutGroup
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.eventbus.event;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.event.DefaultEvent;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
|
||||
/**
|
||||
* Event that gets sent when a distribution set gets assigned to a target.
|
||||
@@ -21,10 +21,8 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
public class TargetAssignDistributionSetEvent extends DefaultEvent {
|
||||
|
||||
private final Collection<SoftwareModule> softwareModules;
|
||||
private final String controllerId;
|
||||
private final Target target;
|
||||
private final Long actionId;
|
||||
private final URI targetAdress;
|
||||
private final String targetToken;
|
||||
|
||||
/**
|
||||
* Creates a new {@link TargetAssignDistributionSetEvent}.
|
||||
@@ -33,26 +31,19 @@ public class TargetAssignDistributionSetEvent extends DefaultEvent {
|
||||
* the revision of the event
|
||||
* @param tenant
|
||||
* the tenant of the event
|
||||
* @param controllerId
|
||||
* the ID of the controller
|
||||
* @param target
|
||||
* the assigned {@link Target}
|
||||
* @param actionId
|
||||
* the action id of the assignment
|
||||
* @param softwareModules
|
||||
* the software modules which have been assigned to the target
|
||||
* @param targetAdress
|
||||
* the targetAdress of the target
|
||||
* @param targetToken
|
||||
* the authentication token of the target
|
||||
*/
|
||||
public TargetAssignDistributionSetEvent(final long revision, final String tenant, final String controllerId,
|
||||
final Long actionId, final Collection<SoftwareModule> softwareModules, final URI targetAdress,
|
||||
final String targetToken) {
|
||||
public TargetAssignDistributionSetEvent(final long revision, final String tenant, final Target target,
|
||||
final Long actionId, final Collection<SoftwareModule> softwareModules) {
|
||||
super(revision, tenant);
|
||||
this.controllerId = controllerId;
|
||||
this.target = target;
|
||||
this.actionId = actionId;
|
||||
this.softwareModules = softwareModules;
|
||||
this.targetAdress = targetAdress;
|
||||
this.targetToken = targetToken;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,11 +54,11 @@ public class TargetAssignDistributionSetEvent extends DefaultEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the controllerId of the Target which has been assigned to the
|
||||
* distribution set
|
||||
* @return the {@link Target} which has been assigned to the distribution
|
||||
* set
|
||||
*/
|
||||
public String getControllerId() {
|
||||
return controllerId;
|
||||
public Target getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,12 +67,4 @@ public class TargetAssignDistributionSetEvent extends DefaultEvent {
|
||||
public Collection<SoftwareModule> getSoftwareModules() {
|
||||
return softwareModules;
|
||||
}
|
||||
|
||||
public URI getTargetAdress() {
|
||||
return targetAdress;
|
||||
}
|
||||
|
||||
public String getTargetToken() {
|
||||
return targetToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ public interface ActionStatus extends TenantAwareBaseEntity {
|
||||
* @return current {@link Status#DOWNLOAD} progress if known by the update
|
||||
* server.
|
||||
*/
|
||||
int getDownloadProgressPercent();
|
||||
short getDownloadProgressPercent();
|
||||
|
||||
/**
|
||||
* @return list of message entries that can be added to the
|
||||
* @return immutable list of message entries that in the
|
||||
* {@link ActionStatus}.
|
||||
*/
|
||||
List<String> getMessages();
|
||||
|
||||
@@ -37,6 +37,6 @@ public interface Artifact extends TenantAwareBaseEntity {
|
||||
/**
|
||||
* @return size of the artifact in bytes.
|
||||
*/
|
||||
Long getSize();
|
||||
long getSize();
|
||||
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public class AssignedSoftwareModule implements Serializable {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (assigned ? 1231 : 1237);
|
||||
result = prime * result + (softwareModule == null ? 0 : softwareModule.hashCode());
|
||||
result = prime * result + ((softwareModule == null) ? 0 : softwareModule.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class AssignedSoftwareModule implements Serializable {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof AssignedSoftwareModule)) {
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final AssignedSoftwareModule other = (AssignedSoftwareModule) obj;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -82,14 +83,22 @@ public class AssignmentResult<T extends BaseEntity> {
|
||||
* @return {@link List} of assigned entity.
|
||||
*/
|
||||
public List<T> getAssignedEntity() {
|
||||
return assignedEntity;
|
||||
if (assignedEntity == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(assignedEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link List} of unassigned entity.
|
||||
*/
|
||||
public List<T> getUnassignedEntity() {
|
||||
return unassignedEntity;
|
||||
if (unassignedEntity == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(unassignedEntity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,6 +44,6 @@ public interface BaseEntity extends Serializable, Identifiable<Long> {
|
||||
/**
|
||||
* @return version of the {@link BaseEntity}.
|
||||
*/
|
||||
long getOptLockRevision();
|
||||
int getOptLockRevision();
|
||||
|
||||
}
|
||||
|
||||
@@ -26,10 +26,25 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
public interface DistributionSet extends NamedVersionedEntity {
|
||||
|
||||
/**
|
||||
* @return {@link Set} of assigned {@link DistributionSetTag}s.
|
||||
* @return immutable {@link Set} of assigned {@link DistributionSetTag}s.
|
||||
*/
|
||||
Set<DistributionSetTag> getTags();
|
||||
|
||||
/**
|
||||
* @param tag
|
||||
* to add
|
||||
* @return <code>true</code> if tag could be added sucessfully (i.e. was not
|
||||
* already in the list).
|
||||
*/
|
||||
boolean addTag(final DistributionSetTag tag);
|
||||
|
||||
/**
|
||||
* @param tag
|
||||
* to remove
|
||||
* @return <code>true</code> if tag was in the list and removed
|
||||
*/
|
||||
boolean removeTag(final DistributionSetTag tag);
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the set is deleted and only kept for history
|
||||
* purposes.
|
||||
|
||||
@@ -17,8 +17,8 @@ import java.util.List;
|
||||
public interface DistributionSetTag extends Tag {
|
||||
|
||||
/**
|
||||
* @return {@link List} of {@link DistributionSet}s this {@link Tag} is
|
||||
* assigned to.
|
||||
* @return immutable {@link List} of {@link DistributionSet}s this
|
||||
* {@link Tag} is assigned to.
|
||||
*/
|
||||
List<DistributionSet> getAssignedToDistributionSet();
|
||||
|
||||
|
||||
@@ -27,15 +27,15 @@ public interface DistributionSetType extends NamedEntity {
|
||||
boolean isDeleted();
|
||||
|
||||
/**
|
||||
* @return set of {@link SoftwareModuleType}s that need to be in a
|
||||
* @return immutable set of {@link SoftwareModuleType}s that need to be in a
|
||||
* {@link DistributionSet} of this type to be
|
||||
* {@link DistributionSet#isComplete()}.
|
||||
*/
|
||||
Set<SoftwareModuleType> getMandatoryModuleTypes();
|
||||
|
||||
/**
|
||||
* @return set of optional {@link SoftwareModuleType}s that can be in a
|
||||
* {@link DistributionSet} of this type.
|
||||
* @return immutable set of optional {@link SoftwareModuleType}s that can be
|
||||
* in a {@link DistributionSet} of this type.
|
||||
*/
|
||||
Set<SoftwareModuleType> getOptionalModuleTypes();
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ package org.eclipse.hawkbit.repository.model;
|
||||
/**
|
||||
* Interface for the entity interceptor lifecycle.
|
||||
*/
|
||||
// Exception squid:EmptyStatementUsageCheck - don't want to force users to
|
||||
// impelemnt all methods
|
||||
@SuppressWarnings("squid:EmptyStatementUsageCheck")
|
||||
public interface EntityInterceptor {
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,7 @@ public interface Rollout extends NamedEntity {
|
||||
void setDistributionSet(DistributionSet distributionSet);
|
||||
|
||||
/**
|
||||
* @return list of deployment groups of the rollout.
|
||||
* @return immutable list of deployment groups of the rollout.
|
||||
*/
|
||||
List<RolloutGroup> getRolloutGroups();
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ public interface RolloutGroup extends NamedEntity {
|
||||
/**
|
||||
* @return the total amount of targets containing in this group
|
||||
*/
|
||||
long getTotalTargets();
|
||||
int getTotalTargets();
|
||||
|
||||
/**
|
||||
* @return the totalTargetCountStatus
|
||||
|
||||
@@ -11,6 +11,10 @@ package org.eclipse.hawkbit.repository.model;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Software package as sub element of a {@link DistributionSet}.
|
||||
*
|
||||
*/
|
||||
public interface SoftwareModule extends NamedVersionedEntity {
|
||||
|
||||
/**
|
||||
@@ -40,12 +44,12 @@ public interface SoftwareModule extends NamedVersionedEntity {
|
||||
Optional<LocalArtifact> getLocalArtifactByFilename(String fileName);
|
||||
|
||||
/**
|
||||
* @return the artifacts
|
||||
* @return immutable list of all artifacts
|
||||
*/
|
||||
List<Artifact> getArtifacts();
|
||||
|
||||
/**
|
||||
* @return local artifacts only
|
||||
* @return immutable list of local artifacts only
|
||||
*/
|
||||
List<LocalArtifact> getLocalArtifacts();
|
||||
|
||||
@@ -104,7 +108,8 @@ public interface SoftwareModule extends NamedVersionedEntity {
|
||||
List<SoftwareModuleMetadata> getMetadata();
|
||||
|
||||
/**
|
||||
* @return the assignedTo
|
||||
* @return immutable list of {@link DistributionSet}s the module is assigned
|
||||
* to
|
||||
*/
|
||||
List<DistributionSet> getAssignedTo();
|
||||
|
||||
|
||||
@@ -32,12 +32,12 @@ public interface Target extends NamedEntity {
|
||||
String getControllerId();
|
||||
|
||||
/**
|
||||
* @return assigned {@link TargetTag}s.
|
||||
* @return immutable set of assigned {@link TargetTag}s.
|
||||
*/
|
||||
Set<TargetTag> getTags();
|
||||
|
||||
/**
|
||||
* @return {@link Action} history of the {@link Target}.
|
||||
* @return immutable {@link Action} history of the {@link Target}.
|
||||
*/
|
||||
List<Action> getActions();
|
||||
|
||||
@@ -64,4 +64,19 @@ public interface Target extends NamedEntity {
|
||||
*/
|
||||
void setSecurityToken(String token);
|
||||
|
||||
/**
|
||||
* @param tag
|
||||
* to add
|
||||
* @return <code>true</code> if tag could be added sucessfully (i.e. was not
|
||||
* already in the list).
|
||||
*/
|
||||
public boolean addTag(TargetTag tag);
|
||||
|
||||
/**
|
||||
* @param tag
|
||||
* to remove
|
||||
* @return <code>true</code> if tag was in the list and removed
|
||||
*/
|
||||
public boolean removeTag(TargetTag tag);
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ public interface TargetInfo extends Serializable {
|
||||
|
||||
/**
|
||||
* @return time in {@link TimeUnit#MILLISECONDS} GMT when the {@link Target}
|
||||
* polled the server the last time.
|
||||
* polled the server the last time or <code>null</code> if target
|
||||
* has never queried yet.
|
||||
*/
|
||||
Long getLastTargetQuery();
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.util.List;
|
||||
public interface TargetTag extends Tag {
|
||||
|
||||
/**
|
||||
* @return {@link List} of targets assigned to this {@link Tag}.
|
||||
* @return immutable {@link List} of targets assigned to this {@link Tag}.
|
||||
*/
|
||||
List<Target> getAssignedToTargets();
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.repository.rsql;
|
||||
|
||||
/**
|
||||
* An interface declaration which validates an RSQL based query syntax and
|
||||
* allows providing suggestions e.g. in case of syntax errors or current cursor
|
||||
* position.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RsqlValidationOracle {
|
||||
|
||||
/**
|
||||
* Parses and validates an given RSQL based query syntax and provides
|
||||
* suggestion based on syntax error and cursor positioning.
|
||||
*
|
||||
* @param rsqlQuery
|
||||
* an RSQL based query string to parse.
|
||||
* @param cursorPosition
|
||||
* the position of the cursor to retrieve suggestions at the
|
||||
* position. {@code -1} indicates for no cursor suggestion
|
||||
* @return a validation oracle context providing information about syntax
|
||||
* errors and possible suggestions for fixing the syntax error or at
|
||||
* the cursor position to replace tokens
|
||||
*/
|
||||
ValidationOracleContext suggest(final String rsqlQuery, final int cursorPosition);
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user