Rollouts can be deleted (#436)

* Management UI

Signed-off-by: Melanie Retter <melanie.retter@bosch-si.com>

* Repository

Signed-off-by: Michael Hirsch <michael.hirsch@bosch-si.com>

* Optimisations and scheduler deleting enabled

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Melanie Retter
2017-02-18 07:19:28 +01:00
committed by Kai Zimmermann
parent 804522f966
commit 5628d625e8
159 changed files with 3029 additions and 1737 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 495 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 581 KiB

After

Width:  |  Height:  |  Size: 164 KiB

View File

@@ -74,4 +74,4 @@ A _Distribution Set_ entity may have for example URIs to artifacts, _Software Mo
"metadata": { "metadata": {
"href": "http://localhost:8080/rest/v1/softwaremodules/83/metadata?offset=0&limit=50" "href": "http://localhost:8080/rest/v1/softwaremodules/83/metadata?offset=0&limit=50"
} }
{% endhighlight %} {% endhighlight %}

View File

@@ -83,8 +83,8 @@ Allows to:
Software rollout in large scale, rollout status overview and rollout management. Software rollout in large scale, rollout status overview and rollout management.
## Features explained ## Features explained
- Create, update and start of rollouts. - Create, update, copy and delete of rollouts.
- Pause and resume of rollouts. - Start, pause and resume of rollouts.
- Progress monitoring for the entire rollout and the individual groups. - Progress monitoring for the entire rollout and the individual groups.
- Drill down to see the groups in a rollout and targets in each group. - Drill down to see the groups in a rollout and targets in each group.
- Rollout attributes: - Rollout attributes:

View File

@@ -153,6 +153,7 @@ public class DeviceSimulatorUpdater {
if (device.getProgress() <= 0 && modules != null) { if (device.getProgress() <= 0 && modules != null) {
device.setUpdateStatus(simulateDownloads(device.getTargetSecurityToken())); device.setUpdateStatus(simulateDownloads(device.getTargetSecurityToken()));
if (isErrorResponse(device.getUpdateStatus())) { if (isErrorResponse(device.getUpdateStatus())) {
device.setProgress(1.0);
callback.updateFinished(device, actionId); callback.updateFinished(device, actionId);
eventbus.post(new ProgressUpdate(device)); eventbus.post(new ProgressUpdate(device));
return; return;
@@ -216,8 +217,11 @@ public class DeviceSimulatorUpdater {
private static UpdateStatus downloadUrl(final String url, final String targetToken, final String sha1Hash, private static UpdateStatus downloadUrl(final String url, final String targetToken, final String sha1Hash,
final long size) { final long size) {
LOGGER.debug("Downloading {} with token {}, expected sha1 hash {} and size {}", url,
hideTokenDetails(targetToken), sha1Hash, size); if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Downloading {} with token {}, expected sha1 hash {} and size {}", url,
hideTokenDetails(targetToken), sha1Hash, size);
}
try { try {
return readAndCheckDownloadUrl(url, targetToken, sha1Hash, size); return readAndCheckDownloadUrl(url, targetToken, sha1Hash, size);

View File

@@ -232,7 +232,7 @@ public class SpSenderService extends SenderService {
headers.put(MessageHeaderKey.TENANT, tenant); headers.put(MessageHeaderKey.TENANT, tenant);
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON); headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
actionUpdateStatus.getMessage().addAll(updateResultMessages); actionUpdateStatus.addMessage(updateResultMessages);
actionUpdateStatus.setActionId(actionId); actionUpdateStatus.setActionId(actionId);
return convertMessage(actionUpdateStatus, messageProperties); return convertMessage(actionUpdateStatus, messageProperties);

View File

@@ -17,16 +17,16 @@ spring.datasource.username=root
spring.datasource.password= spring.datasource.password=
spring.datasource.driverClassName=org.mariadb.jdbc.Driver spring.datasource.driverClassName=org.mariadb.jdbc.Driver
spring.datasource.max-active=100 spring.datasource.tomcat.max-active=100
spring.datasource.max-idle=10 spring.datasource.tomcat.max-idle=10
spring.datasource.min-idle=10 spring.datasource.tomcat.min-idle=10
spring.datasource.initial-size=10 spring.datasource.tomcat.initial-size=10
spring.datasource.validation-query=select 1 from dual spring.datasource.tomcat.validation-query=select 1 from dual
spring.datasource.validation-interval=30000 spring.datasource.tomcat.validation-interval=30000
spring.datasource.test-on-borrow=true spring.datasource.tomcat.test-on-borrow=true
spring.datasource.test-on-return=false spring.datasource.tomcat.test-on-return=false
spring.datasource.test-while-idle=true spring.datasource.tomcat.test-while-idle=true
spring.datasource.time-between-eviction-runs-millis=30000 spring.datasource.tomcat.time-between-eviction-runs-millis=30000
spring.datasource.min-evictable-idle-time-millis=60000 spring.datasource.tomcat.min-evictable-idle-time-millis=60000
spring.datasource.max-wait=10000 spring.datasource.tomcat.max-wait=10000
spring.datasource.jmx-enabled=true spring.datasource.tomcat.jmx-enabled=true

View File

@@ -41,6 +41,7 @@ import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -123,7 +124,6 @@ public class ConfigurableScenario {
.createTargetTags(new TagBuilder().name(group).description("Group " + group).build()).getBody() .createTargetTags(new TagBuilder().name(group).description("Group " + group).build()).getBody()
.get(0).getTagId()) .get(0).getTagId())
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private void cleanRepository() { private void cleanRepository() {
@@ -136,11 +136,20 @@ public class ConfigurableScenario {
} }
private void deleteRollouts() { private void deleteRollouts() {
// TODO: complete this as soon as rollouts can be deleted LOGGER.info("Delete Rollouts");
PagedList<MgmtRolloutResponseBody> rollouts;
while ((rollouts = rolloutResource.getRollouts(0, PAGE_SIZE, null, null).getBody()).getTotal() > 0) {
rollouts.getContent().parallelStream().forEach(rollout -> {
rolloutResource.delete(rollout.getRolloutId());
waitUntilRolloutNoLongerExists(rollout.getRolloutId());
});
}
} }
private void deleteSoftwareModules() { private void deleteSoftwareModules() {
LOGGER.info("Delete SoftwareModules");
PagedList<MgmtSoftwareModule> modules; PagedList<MgmtSoftwareModule> modules;
do { do {
modules = softwareModuleResource.getSoftwareModules(0, PAGE_SIZE, null, null).getBody(); modules = softwareModuleResource.getSoftwareModules(0, PAGE_SIZE, null, null).getBody();
@@ -150,6 +159,8 @@ public class ConfigurableScenario {
} }
private void deleteDistributionSets() { private void deleteDistributionSets() {
LOGGER.info("Delete DistributionSets");
PagedList<MgmtDistributionSet> distributionSets; PagedList<MgmtDistributionSet> distributionSets;
do { do {
distributionSets = distributionSetResource.getDistributionSets(0, PAGE_SIZE, null, null).getBody(); distributionSets = distributionSetResource.getDistributionSets(0, PAGE_SIZE, null, null).getBody();
@@ -170,6 +181,8 @@ public class ConfigurableScenario {
} }
private void deleteTargets() { private void deleteTargets() {
LOGGER.info("Delete Targets");
PagedList<MgmtTarget> targets; PagedList<MgmtTarget> targets;
do { do {
targets = targetResource.getTargets(0, PAGE_SIZE, null, null).getBody(); targets = targetResource.getTargets(0, PAGE_SIZE, null, null).getBody();
@@ -203,8 +216,9 @@ public class ConfigurableScenario {
LOGGER.info("Run semi automatic rollout for set {}", set.getDsId()); LOGGER.info("Run semi automatic rollout for set {}", set.getDsId());
// create a Rollout // create a Rollout
final MgmtRolloutResponseBody rolloutResponseBody = rolloutResource.create(new RolloutBuilder() final MgmtRolloutResponseBody rolloutResponseBody = rolloutResource.create(new RolloutBuilder()
.name("Rollout" + set.getName() + set.getVersion()).semiAutomaticGroups(createRolloutGroups(scenario)) .name("SemiAutomaticRollout" + set.getName() + set.getVersion())
.targetFilterQuery("name==*").distributionSetId(set.getDsId()) .semiAutomaticGroups(createRolloutGroups(scenario)).targetFilterQuery("name==*")
.distributionSetId(set.getDsId())
.successThreshold(String.valueOf(scenario.getRolloutSuccessThreshold())).errorThreshold("5").build()) .successThreshold(String.valueOf(scenario.getRolloutSuccessThreshold())).errorThreshold("5").build())
.getBody(); .getBody();
@@ -218,13 +232,15 @@ public class ConfigurableScenario {
} }
private static List<MgmtRolloutGroup> createRolloutGroups(final Scenario scenario) { private static List<MgmtRolloutGroup> createRolloutGroups(final Scenario scenario) {
final List<MgmtRolloutGroup> result = Lists.newArrayListWithCapacity(scenario.getDeviceGroups().size() * 2); final List<MgmtRolloutGroup> result = Lists
.newArrayListWithExpectedSize((scenario.getDeviceGroups().size() * 3) + 1);
scenario.getDeviceGroups().forEach(groupname -> { scenario.getDeviceGroups().forEach(groupname -> {
result.add(createGroup(1, groupname, 10F)); result.add(createGroup(1, groupname, 10F));
result.add(createGroup(2, groupname, 50F)); result.add(createGroup(2, groupname, 50F));
result.add(createGroup(3, groupname, 100F)); result.add(createGroup(3, groupname, 100F));
}); });
result.add(createFinalGroup());
return result; return result;
} }
@@ -238,6 +254,15 @@ public class ConfigurableScenario {
return one; return one;
} }
private static MgmtRolloutGroup createFinalGroup() {
final MgmtRolloutGroup one = new MgmtRolloutGroup();
one.setName("final");
one.setDescription("Group of non tagged devices");
one.setTargetFilterQuery("name==*");
one.setTargetPercentage(100F);
return one;
}
private void runRollout(final MgmtDistributionSet set, final Scenario scenario) { private void runRollout(final MgmtDistributionSet set, final Scenario scenario) {
LOGGER.info("Run rollout for set {}", set.getDsId()); LOGGER.info("Run rollout for set {}", set.getDsId());
// create a Rollout // create a Rollout
@@ -256,6 +281,17 @@ public class ConfigurableScenario {
LOGGER.info("Run rollout for set {} -> Done", set.getDsId()); LOGGER.info("Run rollout for set {} -> Done", set.getDsId());
} }
private void waitUntilRolloutNoLongerExists(final Long id) {
do {
try {
TimeUnit.SECONDS.sleep(5);
} catch (final InterruptedException e) {
LOGGER.warn("Interrupted!");
Thread.currentThread().interrupt();
}
} while (rolloutResource.getRollout(id).getStatusCode() != HttpStatus.NOT_FOUND);
}
private void waitUntilRolloutIsComplete(final Long id) { private void waitUntilRolloutIsComplete(final Long id) {
do { do {
try { try {

View File

@@ -67,7 +67,7 @@ public class MgmtUiAutoConfiguration {
* UI has an own strategy. * UI has an own strategy.
* *
* @param applicationContext * @param applicationContext
* the context to add the listener * the context to add the listener to
* @param executorService * @param executorService
* the general scheduler service * the general scheduler service
* @param eventBus * @param eventBus

View File

@@ -16,6 +16,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.integration.support.locks.DefaultLockRegistry;
import org.springframework.integration.support.locks.LockRegistry;
/** /**
* Auto-Configuration for enabling JPA repository. * Auto-Configuration for enabling JPA repository.
@@ -36,4 +38,9 @@ public class JpaRepositoryAutoConfiguration {
return new VirtualPropertyResolver(); return new VirtualPropertyResolver();
} }
@Bean
@ConditionalOnMissingBean
public LockRegistry lockRegistry() {
return new DefaultLockRegistry();
}
} }

View File

@@ -13,7 +13,6 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@@ -101,16 +100,16 @@ public final class DataConversionHelper {
} }
static DdiControllerBase fromTarget(final Target target, final Optional<Action> action, static DdiControllerBase fromTarget(final Target target, final Action action,
final String defaultControllerPollTime, final TenantAware tenantAware) { final String defaultControllerPollTime, final TenantAware tenantAware) {
final DdiControllerBase result = new DdiControllerBase( final DdiControllerBase result = new DdiControllerBase(
new DdiConfig(new DdiPolling(defaultControllerPollTime))); new DdiConfig(new DdiPolling(defaultControllerPollTime)));
if (action.isPresent()) { if (action != null) {
if (action.get().isCancelingOrCanceled()) { if (action.isCancelingOrCanceled()) {
result.add(linkTo( result.add(linkTo(
methodOn(DdiRootController.class, tenantAware.getCurrentTenant()).getControllerCancelAction( methodOn(DdiRootController.class, tenantAware.getCurrentTenant()).getControllerCancelAction(
tenantAware.getCurrentTenant(), target.getControllerId(), action.get().getId())) tenantAware.getCurrentTenant(), target.getControllerId(), action.getId()))
.withRel(DdiRestConstants.CANCEL_ACTION)); .withRel(DdiRestConstants.CANCEL_ACTION));
} else { } else {
// we need to add the hashcode here of the actionWithStatus // we need to add the hashcode here of the actionWithStatus
@@ -120,7 +119,7 @@ public final class DataConversionHelper {
// response because of eTags. // response because of eTags.
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant()) result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getControllerBasedeploymentAction(tenantAware.getCurrentTenant(), target.getControllerId(), .getControllerBasedeploymentAction(tenantAware.getCurrentTenant(), target.getControllerId(),
action.get().getId(), calculateEtag(action.get()))) action.getId(), calculateEtag(action)))
.withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION)); .withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION));
} }
} }

View File

@@ -21,6 +21,7 @@ import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.exception.ArtifactBinaryNotFoundException;
import org.eclipse.hawkbit.repository.exception.SoftwareModuleNotAssignedToTargetException; import org.eclipse.hawkbit.repository.exception.SoftwareModuleNotAssignedToTargetException;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -88,7 +89,8 @@ public class DdiArtifactStoreController implements DdiDlArtifactStoreControllerR
if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) { if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) {
result = new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED); result = new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
} else { } else {
final DbArtifact file = artifactManagement.loadArtifactBinary(artifact.getSha1Hash()); final DbArtifact file = artifactManagement.loadArtifactBinary(artifact.getSha1Hash())
.orElseThrow(() -> new ArtifactBinaryNotFoundException(artifact.getSha1Hash()));
// we set a download status only if we are aware of the // we set a download status only if we are aware of the
// targetid, i.e. authenticated and not anonymous // targetid, i.e. authenticated and not anonymous

View File

@@ -36,6 +36,7 @@ import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate; import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
import org.eclipse.hawkbit.repository.exception.ArtifactBinaryNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.SoftwareModuleNotAssignedToTargetException; import org.eclipse.hawkbit.repository.exception.SoftwareModuleNotAssignedToTargetException;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
@@ -130,7 +131,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(controllerId, IpUtil final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(controllerId, IpUtil
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
return new ResponseEntity<>(DataConversionHelper.fromTarget(target, return new ResponseEntity<>(DataConversionHelper.fromTarget(target,
controllerManagement.findOldestActiveActionByTarget(controllerId), controllerManagement.findOldestActiveActionByTarget(controllerId).orElse(null),
controllerManagement.getPollingTime(), tenantAware), HttpStatus.OK); controllerManagement.getPollingTime(), tenantAware), HttpStatus.OK);
} }
@@ -156,7 +157,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
@SuppressWarnings("squid:S3655") @SuppressWarnings("squid:S3655")
final Artifact artifact = module.getArtifactByFilename(fileName).get(); final Artifact artifact = module.getArtifactByFilename(fileName).get();
final DbArtifact file = artifactManagement.loadArtifactBinary(artifact.getSha1Hash()); final DbArtifact file = artifactManagement.loadArtifactBinary(artifact.getSha1Hash())
.orElseThrow(() -> new ArtifactBinaryNotFoundException(artifact.getSha1Hash()));
final String ifMatch = requestResponseContextHolder.getHttpServletRequest().getHeader("If-Match"); final String ifMatch = requestResponseContextHolder.getHttpServletRequest().getHeader("If-Match");
if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) { if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) {

View File

@@ -65,7 +65,7 @@ public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
savedTarget.getTargetInfo().getControllerAttributes().put("dsafsdf", "sdsds"); savedTarget.getTargetInfo().getControllerAttributes().put("dsafsdf", "sdsds");
final Target updateControllerAttributes = controllerManagament.updateControllerAttributes( final Target updateControllerAttributes = controllerManagement.updateControllerAttributes(
savedTarget.getControllerId(), savedTarget.getTargetInfo().getControllerAttributes()); savedTarget.getControllerId(), savedTarget.getTargetInfo().getControllerAttributes());
// request controller attributes need to be false because we don't want // request controller attributes need to be false because we don't want
// to request the // to request the

View File

@@ -201,10 +201,9 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
checkIfArtifactIsAssignedToTarget(secruityToken, sha1Hash); checkIfArtifactIsAssignedToTarget(secruityToken, sha1Hash);
final Artifact artifact = convertDbArtifact(artifactManagement.loadArtifactBinary(sha1Hash)); final Artifact artifact = convertDbArtifact(artifactManagement.loadArtifactBinary(sha1Hash).orElseThrow(
if (artifact == null) { () -> new EntityNotFoundException(org.eclipse.hawkbit.repository.model.Artifact.class, sha1Hash)));
throw new EntityNotFoundException();
}
authentificationResponse.setArtifact(artifact); authentificationResponse.setArtifact(artifact);
final String downloadId = UUID.randomUUID().toString(); final String downloadId = UUID.randomUUID().toString();
// SHA1 key is set, download by SHA1 // SHA1 key is set, download by SHA1

View File

@@ -33,6 +33,8 @@ import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignment
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.util.IpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message; import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
@@ -50,6 +52,8 @@ import org.springframework.context.event.EventListener;
*/ */
public class AmqpMessageDispatcherService extends BaseAmqpService { public class AmqpMessageDispatcherService extends BaseAmqpService {
private static final Logger LOG = LoggerFactory.getLogger(AmqpMessageDispatcherService.class);
private final ArtifactUrlHandler artifactUrlHandler; private final ArtifactUrlHandler artifactUrlHandler;
private final AmqpSenderService amqpSenderService; private final AmqpSenderService amqpSenderService;
private final SystemSecurityContext systemSecurityContext; private final SystemSecurityContext systemSecurityContext;
@@ -102,6 +106,9 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
return; return;
} }
LOG.debug("targetAssignDistributionSet retrieved for controller {}. I will forward it to DMF broker.",
assignedEvent.getControllerId());
sendUpdateMessageToTarget(assignedEvent.getTenant(), sendUpdateMessageToTarget(assignedEvent.getTenant(),
targetManagement.findTargetByControllerID(assignedEvent.getControllerId()).get(), targetManagement.findTargetByControllerID(assignedEvent.getControllerId()).get(),
assignedEvent.getActionId(), assignedEvent.getModules()); assignedEvent.getActionId(), assignedEvent.getModules());

View File

@@ -163,7 +163,7 @@ public class AmqpControllerAuthenticationTest {
final DbArtifact artifact = new DbArtifact(); final DbArtifact artifact = new DbArtifact();
artifact.setSize(ARTIFACT_SIZE); artifact.setSize(ARTIFACT_SIZE);
artifact.setHashes(new DbArtifactHash(SHA1, "md5 test")); artifact.setHashes(new DbArtifactHash(SHA1, "md5 test"));
when(artifactManagementMock.loadArtifactBinary(SHA1)).thenReturn(artifact); when(artifactManagementMock.loadArtifactBinary(SHA1)).thenReturn(Optional.of(artifact));
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate,
mock(AmqpMessageDispatcherService.class), controllerManagementMock, new JpaEntityFactory()); mock(AmqpMessageDispatcherService.class), controllerManagementMock, new JpaEntityFactory());

View File

@@ -373,7 +373,7 @@ public class AmqpMessageHandlerServiceTest {
when(artifactManagementMock.findFirstArtifactBySHA1(SHA1)).thenReturn(Optional.of(localArtifactMock)); when(artifactManagementMock.findFirstArtifactBySHA1(SHA1)).thenReturn(Optional.of(localArtifactMock));
when(controllerManagementMock.hasTargetArtifactAssigned(securityToken.getControllerId(), SHA1)) when(controllerManagementMock.hasTargetArtifactAssigned(securityToken.getControllerId(), SHA1))
.thenReturn(true); .thenReturn(true);
when(artifactManagementMock.loadArtifactBinary(anyString())).thenReturn(dbArtifactMock); when(artifactManagementMock.loadArtifactBinary(anyString())).thenReturn(Optional.of(dbArtifactMock));
when(dbArtifactMock.getArtifactId()).thenReturn("artifactId"); when(dbArtifactMock.getArtifactId()).thenReturn("artifactId");
when(dbArtifactMock.getSize()).thenReturn(1L); when(dbArtifactMock.getSize()).thenReturn(1L);
when(dbArtifactMock.getHashes()).thenReturn(new DbArtifactHash(SHA1, "md5")); when(dbArtifactMock.getHashes()).thenReturn(new DbArtifactHash(SHA1, "md5"));

View File

@@ -105,6 +105,18 @@ public interface MgmtRolloutRestApi {
MediaType.APPLICATION_JSON_VALUE }) MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<Void> pause(@PathVariable("rolloutId") final Long rolloutId); ResponseEntity<Void> pause(@PathVariable("rolloutId") final Long rolloutId);
/**
* Handles the DELETE request for deleting a rollout.
*
* @param rolloutId
* the ID of the rollout to be deleted.
* @return OK response (200) if rollout could be deleted. In case of any
* exception the corresponding errors occur.
*/
@RequestMapping(method = RequestMethod.DELETE, value = "/{rolloutId}", produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<Void> delete(@PathVariable("rolloutId") final Long rolloutId);
/** /**
* Handles the POST request for resuming a rollout. * Handles the POST request for resuming a rollout.
* *

View File

@@ -16,6 +16,7 @@ import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDownloadArtifactRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtDownloadArtifactRestApi;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.exception.ArtifactBinaryNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -67,7 +68,8 @@ public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi
} }
final Artifact artifact = module.getArtifact(artifactId).get(); final Artifact artifact = module.getArtifact(artifactId).get();
final DbArtifact file = artifactManagement.loadArtifactBinary(artifact.getSha1Hash()); final DbArtifact file = artifactManagement.loadArtifactBinary(artifact.getSha1Hash())
.orElseThrow(() -> new ArtifactBinaryNotFoundException(artifact.getSha1Hash()));
final HttpServletRequest request = requestResponseContextHolder.getHttpServletRequest(); final HttpServletRequest request = requestResponseContextHolder.getHttpServletRequest();
final String ifMatch = request.getHeader("If-Match"); final String ifMatch = request.getHeader("If-Match");
if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) { if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) {

View File

@@ -80,9 +80,9 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
final Page<Rollout> findModulesAll; final Page<Rollout> findModulesAll;
if (rsqlParam != null) { if (rsqlParam != null) {
findModulesAll = this.rolloutManagement.findAllByPredicate(rsqlParam, pageable); findModulesAll = this.rolloutManagement.findAllByPredicate(rsqlParam, pageable, false);
} else { } else {
findModulesAll = this.rolloutManagement.findAll(pageable); findModulesAll = this.rolloutManagement.findAll(pageable, false);
} }
final List<MgmtRolloutResponseBody> rest = MgmtRolloutMapper.toResponseRollout(findModulesAll.getContent()); final List<MgmtRolloutResponseBody> rest = MgmtRolloutMapper.toResponseRollout(findModulesAll.getContent());
@@ -140,6 +140,12 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }
@Override
public ResponseEntity<Void> delete(@PathVariable("rolloutId") final Long rolloutId) {
this.rolloutManagement.deleteRollout(rolloutId);
return ResponseEntity.ok().build();
}
@Override @Override
public ResponseEntity<Void> resume(@PathVariable("rolloutId") final Long rolloutId) { public ResponseEntity<Void> resume(@PathVariable("rolloutId") final Long rolloutId) {
this.rolloutManagement.resumeRollout(rolloutId); this.rolloutManagement.resumeRollout(rolloutId);

View File

@@ -16,6 +16,7 @@ import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@@ -238,7 +239,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
@Step @Step
private void retrieveAndVerifyRolloutInRunning(final Rollout rollout) throws Exception { private void retrieveAndVerifyRolloutInRunning(final Rollout rollout) throws Exception {
rolloutManagement.checkStartingRollouts(0); rolloutManagement.handleRollouts();
mvc.perform(get("/rest/v1/rollouts/" + rollout.getId()).accept(MediaType.APPLICATION_JSON)) mvc.perform(get("/rest/v1/rollouts/" + rollout.getId()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -272,7 +273,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
@Step @Step
private void retrieveAndVerifyRolloutInReady(final Rollout rollout) throws Exception { private void retrieveAndVerifyRolloutInReady(final Rollout rollout) throws Exception {
rolloutManagement.checkCreatingRollouts(0); rolloutManagement.handleRollouts();
mvc.perform(get("/rest/v1/rollouts/" + rollout.getId()).accept(MediaType.APPLICATION_JSON)) mvc.perform(get("/rest/v1/rollouts/" + rollout.getId()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -330,7 +331,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
postRollout("rollout2", 5, dsA.getId(), "id==target-0001*", 10); postRollout("rollout2", 5, dsA.getId(), "id==target-0001*", 10);
// Run here, because Scheduler is disabled during tests // Run here, because Scheduler is disabled during tests
rolloutManagement.checkCreatingRollouts(0); rolloutManagement.handleRollouts();
mvc.perform(get("/rest/v1/rollouts").accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) mvc.perform(get("/rest/v1/rollouts").accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
@@ -387,7 +388,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
postRollout("rollout2", 5, dsA.getId(), "id==target*", 20); postRollout("rollout2", 5, dsA.getId(), "id==target*", 20);
// Run here, because Scheduler is disabled during tests // Run here, because Scheduler is disabled during tests
rolloutManagement.checkCreatingRollouts(0); rolloutManagement.handleRollouts();
mvc.perform(get("/rest/v1/rollouts?limit=1").accept(MediaType.APPLICATION_JSON)) mvc.perform(get("/rest/v1/rollouts?limit=1").accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -441,7 +442,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
.andExpect(jsonPath("status", equalTo("starting"))); .andExpect(jsonPath("status", equalTo("starting")));
// Run here, because scheduler is disabled during tests // Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0); rolloutManagement.handleRollouts();
// check rollout is in running state // check rollout is in running state
mvc.perform(get("/rest/v1/rollouts/{rolloutId}", rollout.getId()).accept(MediaType.APPLICATION_JSON)) mvc.perform(get("/rest/v1/rollouts/{rolloutId}", rollout.getId()).accept(MediaType.APPLICATION_JSON))
@@ -467,7 +468,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
.andExpect(status().isOk()); .andExpect(status().isOk());
// Run here, because scheduler is disabled during tests // Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0); rolloutManagement.handleRollouts();
// pausing rollout // pausing rollout
mvc.perform(post("/rest/v1/rollouts/{rolloutId}/pause", rollout.getId())).andDo(MockMvcResultPrinter.print()) mvc.perform(post("/rest/v1/rollouts/{rolloutId}/pause", rollout.getId())).andDo(MockMvcResultPrinter.print())
@@ -497,7 +498,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
.andExpect(status().isOk()); .andExpect(status().isOk());
// Run here, because scheduler is disabled during tests // Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0); rolloutManagement.handleRollouts();
// pausing rollout // pausing rollout
mvc.perform(post("/rest/v1/rollouts/{rolloutId}/pause", rollout.getId())).andDo(MockMvcResultPrinter.print()) mvc.perform(post("/rest/v1/rollouts/{rolloutId}/pause", rollout.getId())).andDo(MockMvcResultPrinter.print())
@@ -531,7 +532,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
.andExpect(status().isOk()); .andExpect(status().isOk());
// Run here, because scheduler is disabled during tests // Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0); rolloutManagement.handleRollouts();
// starting rollout - already started should lead into bad request // starting rollout - already started should lead into bad request
mvc.perform(post("/rest/v1/rollouts/{rolloutId}/start", rollout.getId())).andDo(MockMvcResultPrinter.print()) mvc.perform(post("/rest/v1/rollouts/{rolloutId}/start", rollout.getId())).andDo(MockMvcResultPrinter.print())
@@ -572,7 +573,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
.andExpect(status().isOk()); .andExpect(status().isOk());
// Run here, because scheduler is disabled during tests // Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0); rolloutManagement.handleRollouts();
// retrieve rollout groups from created rollout - 2 groups exists // retrieve rollout groups from created rollout - 2 groups exists
// (amountTargets / groupSize = 2) // (amountTargets / groupSize = 2)
@@ -615,7 +616,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
private void retrieveAndVerifyRolloutGroupInRunningAndScheduled(final Rollout rollout, private void retrieveAndVerifyRolloutGroupInRunningAndScheduled(final Rollout rollout,
final RolloutGroup firstGroup, final RolloutGroup secondGroup) throws Exception { final RolloutGroup firstGroup, final RolloutGroup secondGroup) throws Exception {
rolloutManagement.startRollout(rollout.getId()); rolloutManagement.startRollout(rollout.getId());
rolloutManagement.checkStartingRollouts(0); rolloutManagement.handleRollouts();
mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups/{groupId}", rollout.getId(), firstGroup.getId()) mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups/{groupId}", rollout.getId(), firstGroup.getId())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
@@ -642,7 +643,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
@Step @Step
private void retrieveAndVerifyRolloutGroupInReady(final Rollout rollout, final RolloutGroup firstGroup) private void retrieveAndVerifyRolloutGroupInReady(final Rollout rollout, final RolloutGroup firstGroup)
throws Exception { throws Exception {
rolloutManagement.checkCreatingRollouts(0); rolloutManagement.handleRollouts();
mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups/{groupId}", rollout.getId(), firstGroup.getId()) mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups/{groupId}", rollout.getId(), firstGroup.getId())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
@@ -748,7 +749,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
rolloutManagement.startRollout(rollout.getId()); rolloutManagement.startRollout(rollout.getId());
// Run here, because scheduler is disabled during tests // Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0); rolloutManagement.handleRollouts();
final RolloutGroup firstGroup = rolloutGroupManagement final RolloutGroup firstGroup = rolloutGroupManagement
.findRolloutGroupsByRolloutId(rollout.getId(), new PageRequest(0, 1, Direction.ASC, "id")).getContent() .findRolloutGroupsByRolloutId(rollout.getId(), new PageRequest(0, 1, Direction.ASC, "id")).getContent()
@@ -779,12 +780,29 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
.andExpect(status().isOk()); .andExpect(status().isOk());
// Run here, because scheduler is disabled during tests // Run here, because scheduler is disabled during tests
rolloutManagement.checkStartingRollouts(0); rolloutManagement.handleRollouts();
// check if running // check if running
assertThat(doWithTimeout(() -> getRollout(rollout.getId()), this::success, 60_000, 100)).isNotNull(); assertThat(doWithTimeout(() -> getRollout(rollout.getId()), this::success, 60_000, 100)).isNotNull();
} }
@Test
@Description("Deletion of a rollout")
public void deleteRollout() throws Exception {
final int amountTargets = 10;
testdataFactory.createTargets(amountTargets, "rolloutDelete", "rolloutDelete");
final DistributionSet dsA = testdataFactory.createDistributionSet("");
// create rollout including the created targets with prefix 'rollout'
final Rollout rollout = createRollout("rolloutDelete", 4, dsA.getId(), "controllerId==rolloutDelete*");
// delete rollout
mvc.perform(delete("/rest/v1/rollouts/{rolloutid}", rollout.getId())).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
assertThat(getRollout(rollout.getId()).getStatus()).isEqualTo(RolloutStatus.DELETING);
}
@Test @Test
@Description("Testing that rollout paged list with rsql parameter") @Description("Testing that rollout paged list with rsql parameter")
public void getRolloutWithRSQLParam() throws Exception { public void getRolloutWithRSQLParam() throws Exception {
@@ -922,9 +940,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
.andExpect( .andExpect(
jsonPath("$._links.resume.href", allOf(startsWith(HREF_ROLLOUT_PREFIX), endsWith("/resume")))) jsonPath("$._links.resume.href", allOf(startsWith(HREF_ROLLOUT_PREFIX), endsWith("/resume"))))
.andExpect(jsonPath("$._links.groups.href", .andExpect(jsonPath("$._links.groups.href",
allOf(startsWith(HREF_ROLLOUT_PREFIX), containsString("/deploygroups")))) allOf(startsWith(HREF_ROLLOUT_PREFIX), containsString("/deploygroups"))));
;
} }
private Rollout createRollout(final String name, final int amountGroups, final long distributionSetId, private Rollout createRollout(final String name, final int amountGroups, final long distributionSetId,
@@ -935,16 +951,13 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build()); .successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build());
// Run here, because Scheduler is disabled during tests // Run here, because Scheduler is disabled during tests
rolloutManagement.fillRolloutGroupsWithTargets(rollout.getId()); rolloutManagement.handleRollouts();
return rolloutManagement.findRolloutById(rollout.getId()).get(); return rolloutManagement.findRolloutById(rollout.getId()).get();
} }
protected boolean success(final Rollout result) { protected boolean success(final Rollout result) {
if (null != result && result.getStatus() == RolloutStatus.RUNNING) { return result != null && result.getStatus() == RolloutStatus.RUNNING;
return true;
}
return false;
} }
public Rollout getRollout(final Long rolloutId) throws Exception { public Rollout getRollout(final Long rolloutId) throws Exception {

View File

@@ -162,7 +162,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
try (InputStream fileInputStream = artifactManagement try (InputStream fileInputStream = artifactManagement
.loadArtifactBinary( .loadArtifactBinary(
softwareManagement.findSoftwareModuleById(sm.getId()).get().getArtifacts().get(0).getSha1Hash()) softwareManagement.findSoftwareModuleById(sm.getId()).get().getArtifacts().get(0).getSha1Hash())
.getFileInputStream()) { .get().getFileInputStream()) {
assertTrue("Wrong artifact content", assertTrue("Wrong artifact content",
IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream)); IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream));
} }

View File

@@ -106,7 +106,7 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
final int limitSize = 2; final int limitSize = 2;
final String knownTargetId = "targetId"; final String knownTargetId = "targetId";
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId); final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
controllerManagament.addUpdateActionStatus( controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(actions.get(0).getId()).status(Status.FINISHED).message("test")); entityFactory.actionStatus().create(actions.get(0).getId()).status(Status.FINISHED).message("test"));
final PageRequest pageRequest = new PageRequest(0, 1000, Direction.ASC, ActionFields.ID.getFieldName()); final PageRequest pageRequest = new PageRequest(0, 1000, Direction.ASC, ActionFields.ID.getFieldName());
@@ -1205,7 +1205,7 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
knownControllerAttrs.put("a", "1"); knownControllerAttrs.put("a", "1");
knownControllerAttrs.put("b", "2"); knownControllerAttrs.put("b", "2");
testdataFactory.createTarget(knownTargetId); testdataFactory.createTarget(knownTargetId);
controllerManagament.updateControllerAttributes(knownTargetId, knownControllerAttrs); controllerManagement.updateControllerAttributes(knownTargetId, knownControllerAttrs);
// test query target over rest resource // test query target over rest resource
mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/attributes")) mvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId + "/attributes"))
@@ -1246,7 +1246,7 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
private Target createSingleTarget(final String controllerId, final String name) { private Target createSingleTarget(final String controllerId, final String name) {
targetManagement.createTarget(entityFactory.target().create().controllerId(controllerId).name(name) targetManagement.createTarget(entityFactory.target().create().controllerId(controllerId).name(name)
.description(TARGET_DESCRIPTION_TEST)); .description(TARGET_DESCRIPTION_TEST));
return controllerManagament.updateLastTargetQuery(controllerId, null); return controllerManagement.updateLastTargetQuery(controllerId, null);
} }
/** /**
@@ -1261,7 +1261,7 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
for (int index = 0; index < amount; index++) { for (int index = 0; index < amount; index++) {
final String str = String.valueOf(character); final String str = String.valueOf(character);
targetManagement.createTarget(entityFactory.target().create().controllerId(str).name(str).description(str)); targetManagement.createTarget(entityFactory.target().create().controllerId(str).name(str).description(str));
controllerManagament.updateLastTargetQuery(str, null); controllerManagement.updateLastTargetQuery(str, null);
character++; character++;
} }
} }

View File

@@ -15,7 +15,6 @@ import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.ArtifactBinaryNotFoundException;
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException; import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException; import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
@@ -58,7 +57,10 @@ public interface ArtifactManagement {
* *
* @return uploaded {@link Artifact} * @return uploaded {@link Artifact}
* *
* @throw ArtifactUploadFailedException if upload fails * @throws ArtifactUploadFailedException
* if upload fails
* @throws EntityNotFoundException
* if given software module does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
Artifact createArtifact(@NotNull InputStream inputStream, @NotNull Long moduleId, final String filename, Artifact createArtifact(@NotNull InputStream inputStream, @NotNull Long moduleId, final String filename,
@@ -101,16 +103,20 @@ public interface ArtifactManagement {
String providedMd5Sum, String providedSha1Sum, boolean overrideExisting, String contentType); String providedMd5Sum, String providedSha1Sum, boolean overrideExisting, String contentType);
/** /**
* Garbage collects local artifact binary file if only referenced by given * Garbage collects artifact binaries if only referenced by given
* {@link Artifact} metadata object. * {@link SoftwareModule#getId()} or {@link SoftwareModules} that are marged
* as deleted.
*
* *
* @param artifactId * @param artifactSha1Hash
* the related local artifact * no longer needed
* @param moduleId
* the garbage colelction call is made for
* *
* @return <code>true</code> if an binary was actually garbage collected * @return <code>true</code> if an binary was actually garbage collected
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
boolean clearArtifactBinary(@NotNull Long artifactId); boolean clearArtifactBinary(@NotEmpty String artifactSha1Hash, @NotNull Long moduleId);
/** /**
* Deletes {@link Artifact} based on given id. * Deletes {@link Artifact} based on given id.
@@ -144,6 +150,9 @@ public interface ArtifactManagement {
* @param softwareModuleId * @param softwareModuleId
* software module id. * software module id.
* @return found {@link Artifact} * @return found {@link Artifact}
*
* @throws EntityNotFoundException
* if software module with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER) + SpringEvalExpressions.IS_CONTROLLER)
@@ -179,6 +188,9 @@ public interface ArtifactManagement {
* @param swId * @param swId
* software module id * software module id
* @return Page<Artifact> * @return Page<Artifact>
*
* @throws EntityNotFoundException
* if software module with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<Artifact> findArtifactBySoftwareModule(@NotNull Pageable pageReq, @NotNull Long swId); Page<Artifact> findArtifactBySoftwareModule(@NotNull Pageable pageReq, @NotNull Long swId);
@@ -190,11 +202,9 @@ public interface ArtifactManagement {
* to search for * to search for
* @return loaded {@link DbArtifact} * @return loaded {@link DbArtifact}
* *
* @throws ArtifactBinaryNotFoundException
* if file could not be found in store
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_CONTROLLER_DOWNLOAD) + SpringEvalExpressions.HAS_CONTROLLER_DOWNLOAD)
DbArtifact loadArtifactBinary(@NotEmpty String sha1Hash); Optional<DbArtifact> loadArtifactBinary(@NotEmpty String sha1Hash);
} }

View File

@@ -1,61 +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.repository;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Rollout Management properties.
*
*/
@ConfigurationProperties("hawkbit.autoassign")
public class AutoAssignProperties {
/**
* Autoassign scheduler configuration.
*/
public static class Scheduler {
// used by @Scheduled annotation which needs constant
public static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.autoassign.scheduler.fixedDelay:60000}";
/**
* Schedule where the autoassign scheduler looks necessary state changes
* in milliseconds.
*/
private long fixedDelay = 60000L;
/**
* Set to true to run the autoassign scheduler.
*/
private boolean enabled = true;
public long getFixedDelay() {
return fixedDelay;
}
public void setFixedDelay(final long fixedDelay) {
this.fixedDelay = fixedDelay;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
}
private final Scheduler scheduler = new Scheduler();
public Scheduler getScheduler() {
return scheduler;
}
}

View File

@@ -120,6 +120,9 @@ public interface ControllerManagement {
* @param controllerId * @param controllerId
* identifies the target to retrieve the actions from * identifies the target to retrieve the actions from
* @return a list of actions assigned to given target which are active * @return a list of actions assigned to given target which are active
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Optional<Action> findOldestActiveActionByTarget(@NotNull String controllerId); Optional<Action> findOldestActiveActionByTarget(@NotNull String controllerId);
@@ -157,6 +160,9 @@ public interface ControllerManagement {
* of the the {@link SoftwareModule} that should be assigned to * of the the {@link SoftwareModule} that should be assigned to
* the target * the target
* @return last {@link Action} for given combination * @return last {@link Action} for given combination
*
* @throws EntityNotFoundException
* if target with given ID does not exist
* *
*/ */
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
@@ -184,6 +190,9 @@ public interface ControllerManagement {
* @return {@code true} if the given target has currently or had ever a * @return {@code true} if the given target has currently or had ever a
* relation to the given artifact through the action history, * relation to the given artifact through the action history,
* otherwise {@code false} * otherwise {@code false}
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
boolean hasTargetArtifactAssigned(@NotEmpty String controllerId, @NotEmpty String sha1Hash); boolean hasTargetArtifactAssigned(@NotEmpty String controllerId, @NotEmpty String sha1Hash);
@@ -203,6 +212,9 @@ public interface ControllerManagement {
* @return {@code true} if the given target has currently or had ever a * @return {@code true} if the given target has currently or had ever a
* relation to the given artifact through the action history, * relation to the given artifact through the action history,
* otherwise {@code false} * otherwise {@code false}
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
boolean hasTargetArtifactAssigned(@NotNull Long targetId, @NotEmpty String sha1Hash); boolean hasTargetArtifactAssigned(@NotNull Long targetId, @NotEmpty String sha1Hash);

View File

@@ -149,6 +149,8 @@ public interface DeploymentManagement {
* given {@code fieldNameProvider} * given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException * @throws RSQLParameterSyntaxException
* if the RSQL syntax is wrong * if the RSQL syntax is wrong
* @throws EntityNotFoundException
* if target with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Long countActionsByTarget(@NotNull String rsqlParam, @NotEmpty String controllerId); Long countActionsByTarget(@NotNull String rsqlParam, @NotEmpty String controllerId);
@@ -171,6 +173,9 @@ public interface DeploymentManagement {
* @param controllerId * @param controllerId
* the target associated to the actions to count * the target associated to the actions to count
* @return the count value of found actions associated to the target * @return the count value of found actions associated to the target
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Long countActionsByTarget(@NotEmpty String controllerId); Long countActionsByTarget(@NotEmpty String controllerId);
@@ -309,6 +314,9 @@ public interface DeploymentManagement {
* @param controllerId * @param controllerId
* the target associated with the actions * the target associated with the actions
* @return a list of actions associated with the given target * @return a list of actions associated with the given target
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<Action> findActiveActionsByTarget(@NotEmpty String controllerId); List<Action> findActiveActionsByTarget(@NotEmpty String controllerId);
@@ -320,6 +328,9 @@ public interface DeploymentManagement {
* @param controllerId * @param controllerId
* the target associated with the actions * the target associated with the actions
* @return a list of actions associated with the given target * @return a list of actions associated with the given target
*
* @throws EntityNotFoundException
* if target with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<Action> findInActiveActionsByTarget(@NotEmpty String controllerId); List<Action> findInActiveActionsByTarget(@NotEmpty String controllerId);

View File

@@ -21,7 +21,6 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetWithActionStatus; import org.eclipse.hawkbit.repository.model.TargetWithActionStatus;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
@@ -40,6 +39,9 @@ public interface RolloutGroupManagement {
* @param pageable * @param pageable
* the page request to sort and limit the result * the page request to sort and limit the result
* @return a page of found {@link RolloutGroup}s * @return a page of found {@link RolloutGroup}s
*
* @throws EntityNotFoundException
* of rollout with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(@NotNull Long rolloutId, @NotNull Pageable pageable); Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(@NotNull Long rolloutId, @NotNull Pageable pageable);
@@ -52,14 +54,16 @@ public interface RolloutGroupManagement {
* distribution set we do not create an action for it but the target is in * distribution set we do not create an action for it but the target is in
* the result list of the rollout-group. * the result list of the rollout-group.
* *
* @param pageRequest * @param pageable
* the page request to sort and limit the result * the page request to sort and limit the result
* @param rolloutGroupId * @param rolloutGroupId
* rollout group * rollout group
* @return {@link TargetWithActionStatus} target with action status * @return {@link TargetWithActionStatus} target with action status
* @throws EntityNotFoundException
* if rollout group with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ)
Page<TargetWithActionStatus> findAllTargetsWithActionStatus(@NotNull PageRequest pageRequest, Page<TargetWithActionStatus> findAllTargetsWithActionStatus(@NotNull Pageable pageable,
@NotNull Long rolloutGroupId); @NotNull Long rolloutGroupId);
/** /**
@@ -165,6 +169,9 @@ public interface RolloutGroupManagement {
* @param rolloutGroupId * @param rolloutGroupId
* the rollout group id for the count * the rollout group id for the count
* @return the target rollout group count * @return the target rollout group count
*
* @throws EntityNotFoundException
* if rollout group with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Long countTargetsOfRolloutsGroup(@NotNull Long rolloutGroupId); Long countTargetsOfRolloutsGroup(@NotNull Long rolloutGroupId);

View File

@@ -29,6 +29,7 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus; import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions; import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation; import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
import org.eclipse.hawkbit.repository.model.Target;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -44,72 +45,37 @@ import org.springframework.util.concurrent.ListenableFuture;
public interface RolloutManagement { public interface RolloutManagement {
/** /**
* Checking running rollouts. Rollouts which are checked updating the * Process rollout based on its current {@link Rollout#getStatus()}.
* lastCheck to indicate that the current instance is handling the specific *
* rollout. This code should run as system-code. * For {@link RolloutStatus#CREATING} that means creating the
* * {@link RolloutGroup}s with {@link Target}s and when finished switch to
* <pre> * {@link RolloutStatus#READY}.
* {@code *
* SystemSecurityContext.runAsSystem(new Callable<Void>() { * For {@link RolloutStatus#READY} that means switching to
* public Void call() throws Exception { * {@link RolloutStatus#STARTING} if the {@link Rollout#getStartAt()} is set
* //run system-code * and time of calling this method is beyond this point in time. This auto
* } * start mechanism is optional. Call {@link #startRollout(Long)} otherwise.
* }); *
* } * For {@link RolloutStatus#STARTING} that means starting the first
* </pre> * {@link RolloutGroup}s in line and when finished switch to
* * {@link RolloutStatus#RUNNING}.
* This method is intended to be called by a scheduler. And must be running *
* in an transaction so it's splitted from the scheduler. * For {@link RolloutStatus#RUNNING} that means checking to activate further
* * groups based on the defined thresholds. Switched to
* Rollouts which are currently running are investigated, by means the * {@link RolloutStatus#FINISHED} is all groups are finished.
* error- and finish condition of running groups in this rollout are *
* evaluated. * For {@link RolloutStatus#DELETING} that means either soft delete in case
* * rollout was already {@link RolloutStatus#RUNNING} which results in status
* @param delayBetweenChecks * change {@link RolloutStatus#DELETED} or hard delete from the persistence
* the time in milliseconds of the delay between the further and * otherwise.
* this check. This check is only applied if the last check is *
* less than (lastcheck-delay).
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void checkRunningRollouts(long delayBetweenChecks); void handleRollouts();
/** /**
* Checking Rollouts that are currently being created with asynchronous * Counts all {@link Rollout}s in the repository that are not marked as
* assignment of targets to the Rollout Groups. * deleted.
*
* @param delayBetweenChecks
* the time in milliseconds of the delay between the further and
* this check. This check is only applied if the last check is
* less than (lastcheck-delay).
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void checkCreatingRollouts(long delayBetweenChecks);
/**
* Checking Rollouts that are currently being started with asynchronous
* creation of actions to the targets of a group.
*
* @param delayBetweenChecks
* the time in milliseconds of the delay between the further and
* this check. This check is only applied if the last check is
* less than (lastcheck-delay).
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void checkStartingRollouts(long delayBetweenChecks);
/**
* Checking Rollouts that are currently ready for an auto start.
*
* @param delayBetweenChecks
* the time in milliseconds of the delay between the further and
* this check. This check is only applied if the last check is
* less than (lastcheck-delay).
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void checkReadyRollouts(long delayBetweenChecks);
/**
* Counts all {@link Rollout}s in the repository.
* *
* @return number of roll outs * @return number of roll outs
*/ */
@@ -212,44 +178,30 @@ public interface RolloutManagement {
ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(List<RolloutGroupCreate> groups, ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(List<RolloutGroupCreate> groups,
String targetFilter, Long createdAt); String targetFilter, Long createdAt);
/**
* Can be called on a Rollout in {@link RolloutStatus#CREATING} to
* automatically fill it with targets.
*
* Works through all Rollout groups in {@link RolloutGroupStatus#CREATING}
* and fills them with remaining targets until the supposed amount of
* targets for the group is reached. Targets are added to a group when they
* match the overall {@link Rollout#getTargetFilterQuery()} and the
* {@link RolloutGroup#getTargetFilterQuery()} and not more than
* {@link RolloutGroup#getTargetPercentage()} are assigned to the group.
*
* @param rollout
* the rollout
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void fillRolloutGroupsWithTargets(@NotNull Long rollout);
/** /**
* Retrieves all rollouts. * Retrieves all rollouts.
* *
* @param pageable * @param pageable
* the page request to sort and limit the result * the page request to sort and limit the result
* @param deleted
* flag if deleted rollouts should be included
* @return a page of found rollouts * @return a page of found rollouts
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Page<Rollout> findAll(@NotNull Pageable pageable); Page<Rollout> findAll(@NotNull Pageable pageable, boolean deleted);
/** /**
* Get count of targets in different status in rollout. * Get count of targets in different status in rollout.
* *
* @param pageable * @param pageable
* the page request to sort and limit the result * the page request to sort and limit the result
* @param deleted
* flag if deleted rollouts should be included
* @return a list of rollouts with details of targets count for different * @return a list of rollouts with details of targets count for different
* statuses * statuses
*
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Page<Rollout> findAllRolloutsWithDetailedStatus(@NotNull Pageable pageable); Page<Rollout> findAllRolloutsWithDetailedStatus(@NotNull Pageable pageable, boolean deleted);
/** /**
* Retrieves all rollouts found by the given specification. * Retrieves all rollouts found by the given specification.
@@ -258,6 +210,8 @@ public interface RolloutManagement {
* the specification to filter rollouts * the specification to filter rollouts
* @param pageable * @param pageable
* the page request to sort and limit the result * the page request to sort and limit the result
* @param deleted
* flag if deleted rollouts should be included
* @return a page of found rollouts * @return a page of found rollouts
* *
* @throws RSQLParameterUnsupportedFieldException * @throws RSQLParameterUnsupportedFieldException
@@ -267,7 +221,7 @@ public interface RolloutManagement {
* if the RSQL syntax is wrong * if the RSQL syntax is wrong
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Page<Rollout> findAllByPredicate(@NotNull String rsqlParam, @NotNull Pageable pageable); Page<Rollout> findAllByPredicate(@NotNull String rsqlParam, @NotNull Pageable pageable, boolean deleted);
/** /**
* Finds rollouts by given text in name or description. * Finds rollouts by given text in name or description.
@@ -276,11 +230,14 @@ public interface RolloutManagement {
* the page request to sort and limit the result * the page request to sort and limit the result
* @param searchText * @param searchText
* search text which matches name or description of rollout * search text which matches name or description of rollout
* @param deleted
* flag if deleted rollouts should be included
* @return the founded rollout or {@code null} if rollout with given ID does * @return the founded rollout or {@code null} if rollout with given ID does
* not exists * not exists
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
Slice<Rollout> findRolloutWithDetailedStatusByFilters(@NotNull Pageable pageable, @NotEmpty String searchText); Slice<Rollout> findRolloutWithDetailedStatusByFilters(@NotNull Pageable pageable, @NotEmpty String searchText,
boolean deleted);
/** /**
* Retrieves a specific rollout by its ID. * Retrieves a specific rollout by its ID.
@@ -309,6 +266,8 @@ public interface RolloutManagement {
* *
* @param rolloutId * @param rolloutId
* rollout id * rollout id
* @param deleted
* flag if deleted rollouts should be included
* @return rollout details of targets count for different statuses * @return rollout details of targets count for different statuses
* *
* *
@@ -417,4 +376,15 @@ public interface RolloutManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
Rollout updateRollout(@NotNull RolloutUpdate update); Rollout updateRollout(@NotNull RolloutUpdate update);
/**
* Deletes a rollout. A rollout might be deleted asynchronously by
* indicating the rollout by {@link RolloutStatus#DELETING}
*
*
* @param rolloutId
* the ID of the rollout to be deleted
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void deleteRollout(long rolloutId);
} }

View File

@@ -1,88 +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.repository;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Rollout Management properties.
*
*/
@ConfigurationProperties("hawkbit.rollout")
public class RolloutProperties {
// used by @Scheduled annotation which needs constant
public static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.scheduler.fixedDelay:30000}";
// used by @Scheduled annotation which needs constant
public static final String PROP_CREATING_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.creatingScheduler.fixedDelay:2000}";
// used by @Scheduled annotation which needs constant
public static final String PROP_STARTING_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.startingScheduler.fixedDelay:2000}";
// used by @Scheduled annotation which needs constant
public static final String PROP_READY_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.readyScheduler.fixedDelay:30000}";
/**
* Rollout scheduler configuration.
*/
public static class Scheduler {
/**
* Schedule where the rollout scheduler looks necessary state changes in
* milliseconds.
*/
private long fixedDelay;
private boolean enabled = true;
public Scheduler(final long fixedDelay) {
this.fixedDelay = fixedDelay;
}
public long getFixedDelay() {
return fixedDelay;
}
public void setFixedDelay(final long fixedDelay) {
this.fixedDelay = fixedDelay;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
}
private final Scheduler scheduler = new Scheduler(30000L);
private final Scheduler creatingScheduler = new Scheduler(2000L);
private final Scheduler startingScheduler = new Scheduler(2000L);
private final Scheduler readyScheduler = new Scheduler(30000L);
public Scheduler getScheduler() {
return scheduler;
}
public Scheduler getCreatingScheduler() {
return creatingScheduler;
}
public Scheduler getStartingScheduler() {
return startingScheduler;
}
public Scheduler getReadyScheduler() {
return readyScheduler;
}
}

View File

@@ -0,0 +1,46 @@
/**
* 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.event.remote;
import org.eclipse.hawkbit.repository.event.remote.RemoteIdEvent;
import org.eclipse.hawkbit.repository.model.Rollout;
/**
*
* Defines the remote event of deleting a {@link Rollout}.
*/
public class RolloutDeletedEvent extends RemoteIdEvent {
private static final long serialVersionUID = 1L;
/**
* Default constructor.
*/
public RolloutDeletedEvent() {
// for serialization libs like jackson
}
/**
* Constructor for json serialization.
*
* @param tenant
* the tenant
* @param entityId
* the entity id
* @param entityClass
* the entity class
* @param applicationId
* the origin application id
*/
public RolloutDeletedEvent(final String tenant, final Long entityId, final String entityClass,
final String applicationId) {
super(entityId, tenant, entityClass, applicationId);
}
}

View File

@@ -0,0 +1,56 @@
/**
* 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.event.remote.entity;
import org.eclipse.hawkbit.repository.model.Action;
/**
* Defines the remote event of creating a new {@link Action}.
*/
public abstract class AbstractActionEvent extends RemoteEntityEvent<Action> {
private static final long serialVersionUID = 1L;
private Long rolloutId;
private Long rolloutGroupId;
/**
* Default constructor.
*/
public AbstractActionEvent() {
// for serialization libs like jackson
}
/**
* Constructor
*
* @param action
* the created action
* @param rolloutId
* rollout identifier (optional)
* @param rolloutGroupId
* rollout group identifier (optional)
* @param applicationId
* the origin application id
*/
public AbstractActionEvent(final Action action, final Long rolloutId, final Long rolloutGroupId,
final String applicationId) {
super(action, applicationId);
this.rolloutId = rolloutId;
this.rolloutGroupId = rolloutGroupId;
}
public Long getRolloutId() {
return rolloutId;
}
public Long getRolloutGroupId() {
return rolloutGroupId;
}
}

View File

@@ -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.repository.event.remote.entity;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
/**
* TenantAwareEvent definition which is been published in case a rollout group
* has been created for a specific rollout or updated.
*
*/
public abstract class AbstractRolloutGroupEvent extends RemoteEntityEvent<RolloutGroup> {
private static final long serialVersionUID = 1L;
private Long rolloutId;
/**
* Default constructor.
*/
public AbstractRolloutGroupEvent() {
// for serialization libs like jackson
}
public AbstractRolloutGroupEvent(final RolloutGroup rolloutGroup, final Long rolloutId,
final String applicationId) {
super(rolloutGroup, applicationId);
this.rolloutId = rolloutId;
}
public Long getRolloutId() {
return rolloutId;
}
}

View File

@@ -13,8 +13,8 @@ import org.eclipse.hawkbit.repository.model.Action;
/** /**
* Defines the remote event of creating a new {@link Action}. * Defines the remote event of creating a new {@link Action}.
*/ */
public class ActionCreatedEvent extends RemoteEntityEvent<Action> { public class ActionCreatedEvent extends AbstractActionEvent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 2L;
/** /**
* Default constructor. * Default constructor.
@@ -28,11 +28,16 @@ public class ActionCreatedEvent extends RemoteEntityEvent<Action> {
* *
* @param action * @param action
* the created action * the created action
* @param rolloutId
* rollout identifier (optional)
* @param rolloutGroupId
* rollout group identifier (optional)
* @param applicationId * @param applicationId
* the origin application id * the origin application id
*/ */
public ActionCreatedEvent(final Action action, final String applicationId) { public ActionCreatedEvent(final Action action, final Long rolloutId, final Long rolloutGroupId,
super(action, applicationId); final String applicationId) {
super(action, rolloutId, rolloutGroupId, applicationId);
} }
} }

View File

@@ -13,8 +13,8 @@ import org.eclipse.hawkbit.repository.model.Action;
/** /**
* Defines the remote event of updated a {@link Action}. * Defines the remote event of updated a {@link Action}.
*/ */
public class ActionUpdatedEvent extends RemoteEntityEvent<Action> { public class ActionUpdatedEvent extends AbstractActionEvent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 2L;
/** /**
* Default constructor. * Default constructor.
@@ -28,11 +28,16 @@ public class ActionUpdatedEvent extends RemoteEntityEvent<Action> {
* *
* @param action * @param action
* the updated action * the updated action
* @param rolloutId
* rollout identifier (optional)
* @param rolloutGroupId
* rollout group identifier (optional)
* @param applicationId * @param applicationId
* the origin application id * the origin application id
*/ */
public ActionUpdatedEvent(final Action action, final String applicationId) { public ActionUpdatedEvent(final Action action, final Long rolloutId, final Long rolloutGroupId,
super(action, applicationId); final String applicationId) {
super(action, rolloutId, rolloutGroupId, applicationId);
} }
} }

View File

@@ -15,12 +15,9 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
* has been created for a specific rollout. * has been created for a specific rollout.
* *
*/ */
public class RolloutGroupCreatedEvent extends RemoteEntityEvent<RolloutGroup> { public class RolloutGroupCreatedEvent extends AbstractRolloutGroupEvent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private Long rolloutId;
/** /**
* Default constructor. * Default constructor.
*/ */
@@ -33,16 +30,12 @@ public class RolloutGroupCreatedEvent extends RemoteEntityEvent<RolloutGroup> {
* *
* @param rolloutGroup * @param rolloutGroup
* the updated rolloutGroup * the updated rolloutGroup
* @param rolloutId
* of the related rollout
* @param applicationId * @param applicationId
* the origin application id * the origin application id
*/ */
public RolloutGroupCreatedEvent(final RolloutGroup rolloutGroup, final String applicationId) { public RolloutGroupCreatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId, final String applicationId) {
super(rolloutGroup, applicationId); super(rolloutGroup, rolloutId, applicationId);
this.rolloutId = rolloutGroup.getRollout().getId();
} }
public Long getRolloutId() {
return rolloutId;
}
} }

View File

@@ -13,9 +13,9 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
/** /**
* Defines the remote event of updated a {@link RolloutGroup}. * Defines the remote event of updated a {@link RolloutGroup}.
*/ */
public class RolloutGroupUpdatedEvent extends RemoteEntityEvent<RolloutGroup> { public class RolloutGroupUpdatedEvent extends AbstractRolloutGroupEvent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 2L;
/** /**
* Default constructor. * Default constructor.
@@ -29,11 +29,13 @@ public class RolloutGroupUpdatedEvent extends RemoteEntityEvent<RolloutGroup> {
* *
* @param rolloutGroup * @param rolloutGroup
* the updated rolloutGroup * the updated rolloutGroup
* @param rolloutId
* of the related rollout
* @param applicationId * @param applicationId
* the origin application id * the origin application id
*/ */
public RolloutGroupUpdatedEvent(final RolloutGroup rolloutGroup, final String applicationId) { public RolloutGroupUpdatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId, final String applicationId) {
super(rolloutGroup, applicationId); super(rolloutGroup, rolloutId, applicationId);
} }
} }

View File

@@ -18,9 +18,6 @@ import org.eclipse.hawkbit.exception.SpServerError;
*/ */
public final class ArtifactUploadFailedException extends AbstractServerRtException { public final class ArtifactUploadFailedException extends AbstractServerRtException {
/**
*
*/
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**

View File

@@ -19,6 +19,10 @@ import org.springframework.hateoas.Identifiable;
*/ */
public interface BaseEntity extends Serializable, Identifiable<Long> { public interface BaseEntity extends Serializable, Identifiable<Long> {
static Long getIdOrNull(final BaseEntity entity) {
return entity == null ? null : entity.getId();
}
/** /**
* @return time in {@link TimeUnit#MILLISECONDS} when the {@link BaseEntity} * @return time in {@link TimeUnit#MILLISECONDS} when the {@link BaseEntity}
* was created. * was created.

View File

@@ -26,6 +26,12 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus.Status;
*/ */
public interface Rollout extends NamedEntity { public interface Rollout extends NamedEntity {
/**
* @return <code>true</code> if the rollout is deleted and only kept for
* history purposes.
*/
boolean isDeleted();
/** /**
* @return {@link DistributionSet} that is rolled out * @return {@link DistributionSet} that is rolled out
*/ */
@@ -60,11 +66,11 @@ public interface Rollout extends NamedEntity {
long getForcedTime(); long getForcedTime();
/** /**
* @return Timestamp when the rollout should be started automatically. Can be null. * @return Timestamp when the rollout should be started automatically. Can
* be null.
*/ */
Long getStartAt(); Long getStartAt();
/** /**
* @return number of {@link Target}s in this rollout. * @return number of {@link Target}s in this rollout.
*/ */
@@ -123,9 +129,22 @@ public interface Rollout extends NamedEntity {
*/ */
FINISHED, FINISHED,
/**
* Rollout is under deletion.
*/
DELETING,
/**
* Rollout has been deleted. This state is only set in case of a
* soft-deletion of the rollout which keeps references, in case of an
* hard-deletion of a rollout the rollout-entry itself is deleted.
*/
DELETED,
/** /**
* Rollout could not be created due to errors, might be a database * Rollout could not be created due to errors, might be a database
* problem during asynchronous creating. * problem during asynchronous creating.
*
* @deprecated legacy status is not used anymore * @deprecated legacy status is not used anymore
*/ */
@Deprecated @Deprecated
@@ -134,6 +153,7 @@ public interface Rollout extends NamedEntity {
/** /**
* Rollout could not be started due to errors, might be database problem * Rollout could not be started due to errors, might be database problem
* during asynchronous starting. * during asynchronous starting.
*
* @deprecated legacy status is not used anymore * @deprecated legacy status is not used anymore
*/ */
@Deprecated @Deprecated

View File

@@ -15,6 +15,7 @@ import java.util.Optional;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetTagDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.DistributionSetTagDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent; import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
@@ -91,12 +92,13 @@ public class EventType {
// download // download
TYPES.put(20, DownloadProgressEvent.class); TYPES.put(20, DownloadProgressEvent.class);
TYPES.put(21, SoftwareModuleCreatedEvent.class); TYPES.put(21, SoftwareModuleCreatedEvent.class);
TYPES.put(22, SoftwareModuleDeletedEvent.class); TYPES.put(22, SoftwareModuleDeletedEvent.class);
TYPES.put(23, SoftwareModuleUpdatedEvent.class); TYPES.put(23, SoftwareModuleUpdatedEvent.class);
TYPES.put(24, TargetPollEvent.class); TYPES.put(24, TargetPollEvent.class);
TYPES.put(25, RolloutDeletedEvent.class);
} }
private int value; private int value;

View File

@@ -0,0 +1,175 @@
/**
* 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;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.validation.ConstraintDeclarationException;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Core functionality for {@link RolloutManagement} implementations.
*
*/
public abstract class AbstractRolloutManagement implements RolloutManagement {
protected final TargetManagement targetManagement;
protected final DeploymentManagement deploymentManagement;
protected final RolloutGroupManagement rolloutGroupManagement;
protected final DistributionSetManagement distributionSetManagement;
protected final ApplicationContext context;
protected final ApplicationEventPublisher eventPublisher;
protected final VirtualPropertyReplacer virtualPropertyReplacer;
protected final PlatformTransactionManager txManager;
protected final TenantAware tenantAware;
protected final LockRegistry lockRegistry;
protected AbstractRolloutManagement(final TargetManagement targetManagement,
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
final ApplicationEventPublisher eventPublisher, final VirtualPropertyReplacer virtualPropertyReplacer,
final PlatformTransactionManager txManager, final TenantAware tenantAware,
final LockRegistry lockRegistry) {
this.targetManagement = targetManagement;
this.deploymentManagement = deploymentManagement;
this.rolloutGroupManagement = rolloutGroupManagement;
this.distributionSetManagement = distributionSetManagement;
this.context = context;
this.eventPublisher = eventPublisher;
this.virtualPropertyReplacer = virtualPropertyReplacer;
this.txManager = txManager;
this.tenantAware = tenantAware;
this.lockRegistry = lockRegistry;
}
protected int runInNewTransaction(final String transactionName, final TransactionCallback<Integer> action) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName(transactionName);
def.setReadOnly(false);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
def.setIsolationLevel(Isolation.READ_UNCOMMITTED.value());
return new TransactionTemplate(txManager, def).execute(action);
}
protected RolloutGroupsValidation validateTargetsInGroups(final List<RolloutGroup> groups, final String baseFilter,
final long totalTargets) {
final List<Long> groupTargetCounts = new ArrayList<>(groups.size());
final Map<String, Long> targetFilterCounts = groups.stream()
.map(group -> RolloutHelper.getGroupTargetFilter(baseFilter, group)).distinct()
.collect(Collectors.toMap(Function.identity(), targetManagement::countTargetByTargetFilterQuery));
long unusedTargetsCount = 0;
for (int i = 0; i < groups.size(); i++) {
final RolloutGroup group = groups.get(i);
final String groupTargetFilter = RolloutHelper.getGroupTargetFilter(baseFilter, group);
RolloutHelper.verifyRolloutGroupTargetPercentage(group.getTargetPercentage());
final long targetsInGroupFilter = targetFilterCounts.get(groupTargetFilter);
final long overlappingTargets = countOverlappingTargetsWithPreviousGroups(baseFilter, groups, group, i,
targetFilterCounts);
final long realTargetsInGroup;
// Assume that targets which were not used in the previous groups
// are used in this group
if (overlappingTargets > 0 && unusedTargetsCount > 0) {
realTargetsInGroup = targetsInGroupFilter - overlappingTargets + unusedTargetsCount;
unusedTargetsCount = 0;
} else {
realTargetsInGroup = targetsInGroupFilter - overlappingTargets;
}
final long reducedTargetsInGroup = Math
.round(group.getTargetPercentage() / 100 * (double) realTargetsInGroup);
groupTargetCounts.add(reducedTargetsInGroup);
unusedTargetsCount += realTargetsInGroup - reducedTargetsInGroup;
}
return new RolloutGroupsValidation(totalTargets, groupTargetCounts);
}
private long countOverlappingTargetsWithPreviousGroups(final String baseFilter, final List<RolloutGroup> groups,
final RolloutGroup group, final int groupIndex, final Map<String, Long> targetFilterCounts) {
// there can't be overlapping targets in the first group
if (groupIndex == 0) {
return 0;
}
final List<RolloutGroup> previousGroups = groups.subList(0, groupIndex);
final String overlappingTargetsFilter = RolloutHelper.getOverlappingWithGroupsTargetFilter(baseFilter,
previousGroups, group);
if (targetFilterCounts.containsKey(overlappingTargetsFilter)) {
return targetFilterCounts.get(overlappingTargetsFilter);
} else {
final long overlappingTargets = targetManagement.countTargetByTargetFilterQuery(overlappingTargetsFilter);
targetFilterCounts.put(overlappingTargetsFilter, overlappingTargets);
return overlappingTargets;
}
}
protected long calculateRemainingTargets(final List<RolloutGroup> groups, final String targetFilter,
final Long createdAt) {
final String baseFilter = RolloutHelper.getTargetFilterQuery(targetFilter, createdAt);
final long totalTargets = targetManagement.countTargetByTargetFilterQuery(baseFilter);
if (totalTargets == 0) {
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
}
final RolloutGroupsValidation validation = validateTargetsInGroups(groups, baseFilter, totalTargets);
return totalTargets - validation.getTargetsInGroups();
}
@Override
@Async
public ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(final List<RolloutGroupCreate> groups,
final String targetFilter, final Long createdAt) {
final String baseFilter = RolloutHelper.getTargetFilterQuery(targetFilter, createdAt);
final long totalTargets = targetManagement.countTargetByTargetFilterQuery(baseFilter);
if (totalTargets == 0) {
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
}
return new AsyncResult<>(validateTargetsInGroups(
groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()), baseFilter, totalTargets));
}
}

View File

@@ -6,34 +6,23 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
import org.eclipse.hawkbit.repository.exception.ConstraintViolationException; import org.eclipse.hawkbit.repository.exception.ConstraintViolationException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException; import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions; import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
/** /**
* A collection of static helper methods for the {@link JpaRolloutManagement} * A collection of static helper methods for the {@link RolloutManagement}
*/ */
final class RolloutHelper { public final class RolloutHelper {
private RolloutHelper() { private RolloutHelper() {
} }
@@ -43,7 +32,7 @@ final class RolloutHelper {
* @param conditions * @param conditions
* input conditions and actions * input conditions and actions
*/ */
static void verifyRolloutGroupConditions(final RolloutGroupConditions conditions) { public static void verifyRolloutGroupConditions(final RolloutGroupConditions conditions) {
if (conditions.getSuccessCondition() == null) { if (conditions.getSuccessCondition() == null) {
throw new ConstraintViolationException("Rollout group is missing success condition"); throw new ConstraintViolationException("Rollout group is missing success condition");
} }
@@ -60,7 +49,7 @@ final class RolloutHelper {
* the input group * the input group
* @return the verified group * @return the verified group
*/ */
static RolloutGroup verifyRolloutGroupHasConditions(final RolloutGroup group) { public static RolloutGroup verifyRolloutGroupHasConditions(final RolloutGroup group) {
if (group.getTargetPercentage() < 1F || group.getTargetPercentage() > 100F) { if (group.getTargetPercentage() < 1F || group.getTargetPercentage() > 100F) {
throw new ConstraintViolationException("Target percentage has to be between 1 and 100"); throw new ConstraintViolationException("Target percentage has to be between 1 and 100");
} }
@@ -74,55 +63,13 @@ final class RolloutHelper {
return group; return group;
} }
/**
* In case the given group is missing conditions or actions, they will be
* set from the supplied default conditions.
*
* @param create
* group to check
* @param conditions
* default conditions and actions
*/
static JpaRolloutGroup prepareRolloutGroupWithDefaultConditions(final RolloutGroupCreate create,
final RolloutGroupConditions conditions) {
final JpaRolloutGroup group = ((JpaRolloutGroupCreate) create).build();
if (group.getSuccessCondition() == null) {
group.setSuccessCondition(conditions.getSuccessCondition());
}
if (group.getSuccessConditionExp() == null) {
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
}
if (group.getSuccessAction() == null) {
group.setSuccessAction(conditions.getSuccessAction());
}
if (group.getSuccessActionExp() == null) {
group.setSuccessActionExp(conditions.getSuccessActionExp());
}
if (group.getErrorCondition() == null) {
group.setErrorCondition(conditions.getErrorCondition());
}
if (group.getErrorConditionExp() == null) {
group.setErrorConditionExp(conditions.getErrorConditionExp());
}
if (group.getErrorAction() == null) {
group.setErrorAction(conditions.getErrorAction());
}
if (group.getErrorActionExp() == null) {
group.setErrorActionExp(conditions.getErrorActionExp());
}
return group;
}
/** /**
* Verify if the supplied amount of groups is in range * Verify if the supplied amount of groups is in range
* *
* @param amountGroup * @param amountGroup
* amount of groups * amount of groups
*/ */
static void verifyRolloutGroupParameter(final int amountGroup) { public static void verifyRolloutGroupParameter(final int amountGroup) {
if (amountGroup <= 0) { if (amountGroup <= 0) {
throw new ConstraintViolationException("the amountGroup must be greater than zero"); throw new ConstraintViolationException("the amountGroup must be greater than zero");
} else if (amountGroup > 500) { } else if (amountGroup > 500) {
@@ -136,7 +83,7 @@ final class RolloutHelper {
* @param percentage * @param percentage
* the percentage * the percentage
*/ */
static void verifyRolloutGroupTargetPercentage(final float percentage) { public static void verifyRolloutGroupTargetPercentage(final float percentage) {
if (percentage <= 0) { if (percentage <= 0) {
throw new ConstraintViolationException("the percentage must be greater than zero"); throw new ConstraintViolationException("the percentage must be greater than zero");
} else if (percentage > 100) { } else if (percentage > 100) {
@@ -152,7 +99,7 @@ final class RolloutHelper {
* Rollout to derive the filter from * Rollout to derive the filter from
* @return resulting target filter query * @return resulting target filter query
*/ */
static String getTargetFilterQuery(final Rollout rollout) { public static String getTargetFilterQuery(final Rollout rollout) {
return getTargetFilterQuery(rollout.getTargetFilterQuery(), rollout.getCreatedAt()); return getTargetFilterQuery(rollout.getTargetFilterQuery(), rollout.getCreatedAt());
} }
@@ -164,7 +111,7 @@ final class RolloutHelper {
* @return a target filter query that only matches targets that were created * @return a target filter query that only matches targets that were created
* after the provided timestamp. * after the provided timestamp.
*/ */
static String getTargetFilterQuery(final String targetFilter, final Long createdAt) { public static String getTargetFilterQuery(final String targetFilter, final Long createdAt) {
if (createdAt != null) { if (createdAt != null) {
return targetFilter + ";createdat=le=" + createdAt.toString(); return targetFilter + ";createdat=le=" + createdAt.toString();
} }
@@ -179,7 +126,7 @@ final class RolloutHelper {
* @param status * @param status
* the Status * the Status
*/ */
static void verifyRolloutInStatus(final Rollout rollout, final Rollout.RolloutStatus status) { public static void verifyRolloutInStatus(final Rollout rollout, final Rollout.RolloutStatus status) {
if (!rollout.getStatus().equals(status)) { if (!rollout.getStatus().equals(status)) {
throw new RolloutIllegalStateException("Rollout is not in status " + status.toString()); throw new RolloutIllegalStateException("Rollout is not in status " + status.toString());
} }
@@ -197,7 +144,7 @@ final class RolloutHelper {
* the group to add * the group to add
* @return list of groups * @return list of groups
*/ */
static List<Long> getGroupsByStatusIncludingGroup(final Rollout rollout, public static List<Long> getGroupsByStatusIncludingGroup(final Rollout rollout,
final RolloutGroup.RolloutGroupStatus status, final RolloutGroup group) { final RolloutGroup.RolloutGroupStatus status, final RolloutGroup group) {
return rollout.getRolloutGroups().stream() return rollout.getRolloutGroups().stream()
.filter(innerGroup -> innerGroup.getStatus().equals(status) || innerGroup.equals(group)) .filter(innerGroup -> innerGroup.getStatus().equals(status) || innerGroup.equals(group))
@@ -211,7 +158,7 @@ final class RolloutHelper {
* the rollout * the rollout
* @return ordered list of groups * @return ordered list of groups
*/ */
static List<RolloutGroup> getOrderedGroups(final Rollout rollout) { public static List<RolloutGroup> getOrderedGroups(final Rollout rollout) {
return rollout.getRolloutGroups().stream().sorted((group1, group2) -> { return rollout.getRolloutGroups().stream().sorted((group1, group2) -> {
if (group1.getId() < group2.getId()) { if (group1.getId() < group2.getId()) {
return -1; return -1;
@@ -232,7 +179,7 @@ final class RolloutHelper {
* @return RSQL string without base filter of the Rollout. Can be an empty * @return RSQL string without base filter of the Rollout. Can be an empty
* string. * string.
*/ */
static String getAllGroupsTargetFilter(final List<RolloutGroup> groups) { public static String getAllGroupsTargetFilter(final List<RolloutGroup> groups) {
if (groups.stream().anyMatch(group -> StringUtils.isEmpty(group.getTargetFilterQuery()))) { if (groups.stream().anyMatch(group -> StringUtils.isEmpty(group.getTargetFilterQuery()))) {
return ""; return "";
} }
@@ -254,7 +201,7 @@ final class RolloutHelper {
* @return RSQL string without base filter of the Rollout. Can be an empty * @return RSQL string without base filter of the Rollout. Can be an empty
* string. * string.
*/ */
static String getOverlappingWithGroupsTargetFilter(final String baseFilter, final List<RolloutGroup> groups, public static String getOverlappingWithGroupsTargetFilter(final String baseFilter, final List<RolloutGroup> groups,
final RolloutGroup group) { final RolloutGroup group) {
final String groupFilter = group.getTargetFilterQuery(); final String groupFilter = group.getTargetFilterQuery();
// when any previous group has the same filter as the target group the // when any previous group has the same filter as the target group the
@@ -283,7 +230,7 @@ final class RolloutHelper {
&& prevGroup.getTargetFilterQuery().equals(groupFilter)); && prevGroup.getTargetFilterQuery().equals(groupFilter));
} }
private static String concatAndTargetFilters(String... filters) { private static String concatAndTargetFilters(final String... filters) {
return "(" + Arrays.stream(filters).collect(Collectors.joining(");(")) + ")"; return "(" + Arrays.stream(filters).collect(Collectors.joining(");(")) + ")";
} }
@@ -308,7 +255,7 @@ final class RolloutHelper {
* @param targetCount * @param targetCount
* the count of left targets * the count of left targets
*/ */
static void verifyRemainingTargets(final long targetCount) { public static void verifyRemainingTargets(final long targetCount) {
if (targetCount > 0) { if (targetCount > 0) {
throw new ConstraintViolationException( throw new ConstraintViolationException(
"Rollout groups don't match all targets that are targeted by the rollout"); "Rollout groups don't match all targets that are targeted by the rollout");
@@ -318,35 +265,11 @@ final class RolloutHelper {
} }
} }
/** public static void checkIfRolloutCanStarted(final Rollout rollout, final Rollout mergedRollout) {
* @param searchText
* search string
* @return criteria specification with a query for name or description of a
* rollout
*/
static Specification<JpaRollout> likeNameOrDescription(final String searchText) {
return (rolloutRoot, query, criteriaBuilder) -> {
final String searchTextToLower = searchText.toLowerCase();
return criteriaBuilder.or(
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(JpaRollout_.name)), searchTextToLower),
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(JpaRollout_.description)),
searchTextToLower));
};
}
static void checkIfRolloutCanStarted(final Rollout rollout, final Rollout mergedRollout) {
if (!(Rollout.RolloutStatus.READY.equals(mergedRollout.getStatus()))) { if (!(Rollout.RolloutStatus.READY.equals(mergedRollout.getStatus()))) {
throw new RolloutIllegalStateException("Rollout can only be started in state ready but current state is " throw new RolloutIllegalStateException("Rollout can only be started in state ready but current state is "
+ rollout.getStatus().name().toLowerCase()); + rollout.getStatus().name().toLowerCase());
} }
} }
static Page<Rollout> convertPage(final Page<JpaRollout> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements());
}
static Slice<Rollout> convertPage(final Slice<JpaRollout> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, 0);
}
} }

View File

@@ -195,6 +195,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* the status which the actions should not have * the status which the actions should not have
* @return the found list of {@link Action}s * @return the found list of {@link Action}s
*/ */
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
@Query("SELECT a FROM JpaAction a WHERE a.active = true AND a.distributionSet.requiredMigrationStep = false AND a.target IN ?1 AND a.status != ?2") @Query("SELECT a FROM JpaAction a WHERE a.active = true AND a.distributionSet.requiredMigrationStep = false AND a.target IN ?1 AND a.status != ?2")
List<JpaAction> findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep( List<JpaAction> findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(
Collection<Long> targetIds, Action.Status notStatus); Collection<Long> targetIds, Action.Status notStatus);
@@ -264,6 +265,44 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
*/ */
Long countByRolloutIdAndRolloutGroupIdAndStatus(Long rolloutId, Long rolloutGroupId, Action.Status status); Long countByRolloutIdAndRolloutGroupIdAndStatus(Long rolloutId, Long rolloutGroupId, Action.Status status);
/**
* Counts all actions referring to a given rollout and status.
*
* @param rolloutId
* the ID of the rollout the actions belong to
* @param status
* the status the actions should have
* @return the count of actions referring to a rollout and are in a given
* status
*/
Long countByRolloutIdAndStatus(Long rolloutId, Action.Status status);
/**
* Returns {@code true} if actions for the given rollout exists, otherwise
* {@code false}
*
* @param rolloutId
* the ID of the rollout the actions belong to
* @return {@code true} if actions for the given rollout exists, otherwise
* {@code false}
*/
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaAction a WHERE a.rollout.id=:rolloutId")
boolean existsByRolloutId(@Param("rolloutId") Long rolloutId);
/**
* Returns {@code true} if actions for the given rollout exists, otherwise
* {@code false}
*
* @param rolloutId
* the ID of the rollout the actions belong to
* @param status
* the action is not to be in
* @return {@code true} if actions for the given rollout exists, otherwise
* {@code false}
*/
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaAction a WHERE a.rollout.id=:rolloutId AND a.status != :status")
boolean existsByRolloutIdAndStatusNotIn(@Param("rolloutId") Long rolloutId, @Param("status") Status status);
/** /**
* Retrieving all actions referring to a given rollout with a specific * Retrieving all actions referring to a given rollout with a specific
* action as parent reference and a specific status. * action as parent reference and a specific status.
@@ -281,6 +320,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* @return the actions referring a specific rollout and a specific parent * @return the actions referring a specific rollout and a specific parent
* rolloutgroup in a specific status * rolloutgroup in a specific status
*/ */
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
Page<Action> findByRolloutAndRolloutGroupParentAndStatus(Pageable pageable, JpaRollout rollout, Page<Action> findByRolloutAndRolloutGroupParentAndStatus(Pageable pageable, JpaRollout rollout,
JpaRolloutGroup rolloutGroupParent, Status actionStatus); JpaRolloutGroup rolloutGroupParent, Status actionStatus);
@@ -296,19 +336,22 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* @return the actions referring a specific rollout and a specific parent * @return the actions referring a specific rollout and a specific parent
* rolloutgroup in a specific status * rolloutgroup in a specific status
*/ */
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
Page<Action> findByRolloutAndRolloutGroupParentIsNullAndStatus(Pageable pageable, JpaRollout rollout, Page<Action> findByRolloutAndRolloutGroupParentIsNullAndStatus(Pageable pageable, JpaRollout rollout,
Status actionStatus); Status actionStatus);
/** /**
* Retrieves all actions for a specific rollout and in a specific status. * Retrieves all actions for a specific rollout and in a specific status.
* *
* @param pageable
* page parameters
* @param rolloutId * @param rolloutId
* the rollout the actions beglong to * the rollout the actions beglong to
* @param actionStatus * @param actionStatus
* the status of the actions * the status of the actions
* @return the actions referring a specific rollout an in a specific status * @return the actions referring a specific rollout an in a specific status
*/ */
List<Action> findByRolloutIdAndStatus(Long rolloutId, Status actionStatus); Page<JpaAction> findByRolloutIdAndStatus(Pageable pageable, Long rolloutId, Status actionStatus);
/** /**
* Get list of objects which has details of status and count of targets in * Get list of objects which has details of status and count of targets in
@@ -354,4 +397,15 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status , COUNT(a.target)) FROM JpaAction a WHERE a.rolloutGroup.id IN ?1 GROUP BY a.rolloutGroup.id, a.status") @Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status , COUNT(a.target)) FROM JpaAction a WHERE a.rolloutGroup.id IN ?1 GROUP BY a.rolloutGroup.id, a.status")
List<TotalTargetCountActionStatus> getStatusCountByRolloutGroupId(List<Long> rolloutGroupId); List<TotalTargetCountActionStatus> getStatusCountByRolloutGroupId(List<Long> rolloutGroupId);
/**
* Deletes all actions with the given IDs.
*
* @param actionIDs
* the IDs of the actions to be deleted.
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("DELETE FROM JpaAction a WHERE a.id IN ?1")
void deleteByIdIn(final Collection<Long> actionIDs);
} }

View File

@@ -12,7 +12,6 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTag;
@@ -72,11 +71,11 @@ public interface DistributionSetRepository
* Finds {@link DistributionSet}s where given {@link SoftwareModule} is * Finds {@link DistributionSet}s where given {@link SoftwareModule} is
* assigned. * assigned.
* *
* @param module * @param moduleId
* to search for * to search for
* @return {@link List} of found {@link DistributionSet}s * @return {@link List} of found {@link DistributionSet}s
*/ */
Long countByModules(JpaSoftwareModule module); Long countByModulesId(Long moduleId);
/** /**
* Finds {@link DistributionSet}s based on given ID that are assigned yet to * Finds {@link DistributionSet}s based on given ID that are assigned yet to

View File

@@ -17,7 +17,6 @@ import org.eclipse.hawkbit.artifact.repository.HashNotMatchException;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash; import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.exception.ArtifactBinaryNotFoundException;
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException; import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException; import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
@@ -104,23 +103,16 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override @Override
@Modifying @Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED) @Transactional(isolation = Isolation.READ_UNCOMMITTED)
public boolean clearArtifactBinary(final Long artifactId) { public boolean clearArtifactBinary(final String sha1Hash, final Long moduleId) {
return clearArtifactBinary(localArtifactRepository.findById(artifactId)
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, artifactId)));
}
private boolean clearArtifactBinary(final JpaArtifact existing) { if (localArtifactRepository.existsWithSha1HashAndSoftwareModuleIdIsNot(sha1Hash, moduleId)) {
// there are still other artifacts that need the binary
for (final Artifact lArtifact : localArtifactRepository.findBySha1Hash(existing.getSha1Hash())) { return false;
if (!lArtifact.getSoftwareModule().isDeleted()
&& Long.compare(lArtifact.getSoftwareModule().getId(), existing.getSoftwareModule().getId()) != 0) {
return false;
}
} }
try { try {
LOG.debug("deleting artifact from repository {}", existing.getSha1Hash()); LOG.debug("deleting artifact from repository {}", sha1Hash);
artifactRepository.deleteBySha1(existing.getSha1Hash()); artifactRepository.deleteBySha1(sha1Hash);
return true; return true;
} catch (final ArtifactStoreException e) { } catch (final ArtifactStoreException e) {
throw new ArtifactDeleteFailedException(e); throw new ArtifactDeleteFailedException(e);
@@ -134,7 +126,7 @@ public class JpaArtifactManagement implements ArtifactManagement {
final JpaArtifact existing = (JpaArtifact) findArtifact(id) final JpaArtifact existing = (JpaArtifact) findArtifact(id)
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, id)); .orElseThrow(() -> new EntityNotFoundException(Artifact.class, id));
clearArtifactBinary(existing); clearArtifactBinary(existing.getSha1Hash(), existing.getSoftwareModule().getId());
((JpaSoftwareModule) existing.getSoftwareModule()).removeArtifact(existing); ((JpaSoftwareModule) existing.getSoftwareModule()).removeArtifact(existing);
softwareModuleRepository.save((JpaSoftwareModule) existing.getSoftwareModule()); softwareModuleRepository.save((JpaSoftwareModule) existing.getSoftwareModule());
@@ -148,6 +140,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override @Override
public Optional<Artifact> findByFilenameAndSoftwareModule(final String filename, final Long softwareModuleId) { public Optional<Artifact> findByFilenameAndSoftwareModule(final String filename, final Long softwareModuleId) {
throwExceptionIfSoftwareModuleDoesNotExist(softwareModuleId);
return localArtifactRepository.findFirstByFilenameAndSoftwareModuleId(filename, softwareModuleId); return localArtifactRepository.findFirstByFilenameAndSoftwareModuleId(filename, softwareModuleId);
} }
@@ -163,13 +157,20 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override @Override
public Page<Artifact> findArtifactBySoftwareModule(final Pageable pageReq, final Long swId) { public Page<Artifact> findArtifactBySoftwareModule(final Pageable pageReq, final Long swId) {
throwExceptionIfSoftwareModuleDoesNotExist(swId);
return localArtifactRepository.findBySoftwareModuleId(pageReq, swId); return localArtifactRepository.findBySoftwareModuleId(pageReq, swId);
} }
private void throwExceptionIfSoftwareModuleDoesNotExist(final Long swId) {
if (!softwareModuleRepository.exists(swId)) {
throw new EntityNotFoundException(SoftwareModule.class, swId);
}
}
@Override @Override
public DbArtifact loadArtifactBinary(final String sha1Hash) { public Optional<DbArtifact> loadArtifactBinary(final String sha1Hash) {
return Optional.ofNullable(artifactRepository.getArtifactBySha1(sha1Hash)) return Optional.ofNullable(artifactRepository.getArtifactBySha1(sha1Hash));
.orElseThrow(() -> new ArtifactBinaryNotFoundException(sha1Hash));
} }
private Artifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename, private Artifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename,

View File

@@ -46,6 +46,7 @@ import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
@@ -85,6 +86,9 @@ public class JpaControllerManagement implements ControllerManagement {
@Autowired @Autowired
private TargetRepository targetRepository; private TargetRepository targetRepository;
@Autowired
private SoftwareModuleRepository softwareModuleRepository;
@Autowired @Autowired
private TargetManagement targetManagement; private TargetManagement targetManagement;
@@ -140,6 +144,9 @@ public class JpaControllerManagement implements ControllerManagement {
@Override @Override
public Optional<Action> getActionForDownloadByTargetAndSoftwareModule(final String controllerId, public Optional<Action> getActionForDownloadByTargetAndSoftwareModule(final String controllerId,
final Long moduleId) { final Long moduleId) {
throwExceptionIfTargetDoesNotExist(controllerId);
throwExceptionIfSoftwareModuleDoesNotExist(moduleId);
final List<Action> action = actionRepository.findActionByTargetAndSoftwareModule(controllerId, moduleId); final List<Action> action = actionRepository.findActionByTargetAndSoftwareModule(controllerId, moduleId);
if (action.isEmpty() || action.get(0).isCancelingOrCanceled()) { if (action.isEmpty() || action.get(0).isCancelingOrCanceled()) {
@@ -149,26 +156,40 @@ public class JpaControllerManagement implements ControllerManagement {
return Optional.ofNullable(action.get(0)); return Optional.ofNullable(action.get(0));
} }
private void throwExceptionIfTargetDoesNotExist(final String controllerId) {
if (!targetRepository.existsByControllerId(controllerId)) {
throw new EntityNotFoundException(Target.class, controllerId);
}
}
private void throwExceptionIfTargetDoesNotExist(final Long targetId) {
if (!targetRepository.exists(targetId)) {
throw new EntityNotFoundException(Target.class, targetId);
}
}
private void throwExceptionIfSoftwareModuleDoesNotExist(final Long moduleId) {
if (!softwareModuleRepository.exists(moduleId)) {
throw new EntityNotFoundException(SoftwareModule.class, moduleId);
}
}
@Override @Override
public boolean hasTargetArtifactAssigned(final String controllerId, final String sha1Hash) { public boolean hasTargetArtifactAssigned(final String controllerId, final String sha1Hash) {
final Optional<Target> target = targetRepository.findByControllerId(controllerId); throwExceptionIfTargetDoesNotExist(controllerId);
if (!target.isPresent()) { return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(controllerId, sha1Hash)) > 0;
return false;
}
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target.get(), sha1Hash)) > 0;
} }
@Override @Override
public boolean hasTargetArtifactAssigned(final Long targetId, final String sha1Hash) { public boolean hasTargetArtifactAssigned(final Long targetId, final String sha1Hash) {
final Target target = targetRepository.findOne(targetId); throwExceptionIfTargetDoesNotExist(targetId);
if (target == null) { return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(targetId, sha1Hash)) > 0;
return false;
}
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target, sha1Hash)) > 0;
} }
@Override @Override
public Optional<Action> findOldestActiveActionByTarget(final String controllerId) { public Optional<Action> findOldestActiveActionByTarget(final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId);
// used in favorite to findFirstByTargetAndActiveOrderByIdAsc due to // used in favorite to findFirstByTargetAndActiveOrderByIdAsc due to
// DATAJPA-841 issue. // DATAJPA-841 issue.
return actionRepository.findFirstByTargetControllerIdAndActive(new Sort(Direction.ASC, "id"), controllerId, return actionRepository.findFirstByTargetControllerIdAndActive(new Sort(Direction.ASC, "id"), controllerId,

View File

@@ -225,7 +225,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
final List<JpaTarget> targets = Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream() final List<JpaTarget> targets = Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream()
.map(ids -> targetRepository .map(ids -> targetRepository
.findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, set.getId()))) .findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, set.getId())))
.flatMap(t -> t.stream()).collect(Collectors.toList()); .flatMap(List::stream).collect(Collectors.toList());
if (targets.isEmpty()) { if (targets.isEmpty()) {
// detaching as it is not necessary to persist the set itself // detaching as it is not necessary to persist the set itself
@@ -456,6 +456,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
final RolloutGroup rolloutGroupParent, final int limit) { final RolloutGroup rolloutGroupParent, final int limit) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition(); final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("startScheduledActions"); def.setName("startScheduledActions");
def.setReadOnly(false);
def.setIsolationLevel(Isolation.READ_UNCOMMITTED.value());
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
return new TransactionTemplate(txManager, def).execute(status -> { return new TransactionTemplate(txManager, def).execute(status -> {
final Page<Action> rolloutGroupActions = findActionsByRolloutAndRolloutGroupParent(rollout, final Page<Action> rolloutGroupActions = findActionsByRolloutAndRolloutGroupParent(rollout,
@@ -599,24 +601,38 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override @Override
public List<Action> findActiveActionsByTarget(final String controllerId) { public List<Action> findActiveActionsByTarget(final String controllerId) {
throwExceptionIfTargetFoesNotExist(controllerId);
return actionRepository.findByActiveAndTarget(controllerId, true); return actionRepository.findByActiveAndTarget(controllerId, true);
} }
@Override @Override
public List<Action> findInActiveActionsByTarget(final String controllerId) { public List<Action> findInActiveActionsByTarget(final String controllerId) {
throwExceptionIfTargetFoesNotExist(controllerId);
return actionRepository.findByActiveAndTarget(controllerId, false); return actionRepository.findByActiveAndTarget(controllerId, false);
} }
@Override @Override
public Long countActionsByTarget(final String controllerId) { public Long countActionsByTarget(final String controllerId) {
throwExceptionIfTargetFoesNotExist(controllerId);
return actionRepository.countByTargetControllerId(controllerId); return actionRepository.countByTargetControllerId(controllerId);
} }
@Override @Override
public Long countActionsByTarget(final String rsqlParam, final String controllerId) { public Long countActionsByTarget(final String rsqlParam, final String controllerId) {
throwExceptionIfTargetFoesNotExist(controllerId);
return actionRepository.count(createSpecificationFor(controllerId, rsqlParam)); return actionRepository.count(createSpecificationFor(controllerId, rsqlParam));
} }
private void throwExceptionIfTargetFoesNotExist(final String controllerId) {
if (!targetRepository.existsByControllerId(controllerId)) {
throw new EntityNotFoundException(Target.class, controllerId);
}
}
@Override @Override
@Modifying @Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED) @Transactional(isolation = Isolation.READ_UNCOMMITTED)

View File

@@ -38,6 +38,7 @@ import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup_; import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup_;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus; import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
@@ -48,7 +49,6 @@ import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
@@ -65,6 +65,9 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
@Autowired @Autowired
private RolloutGroupRepository rolloutGroupRepository; private RolloutGroupRepository rolloutGroupRepository;
@Autowired
private RolloutRepository rolloutRepository;
@Autowired @Autowired
private ActionRepository actionRepository; private ActionRepository actionRepository;
@@ -115,9 +118,19 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
@Override @Override
public Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(final Long rolloutId, final Pageable pageable) { public Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(final Long rolloutId, final Pageable pageable) {
if (!rolloutRepository.exists(rolloutId)) {
throw new EntityNotFoundException(Rollout.class, rolloutId);
}
final Page<JpaRolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutId(rolloutId, pageable); final Page<JpaRolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutId(rolloutId, pageable);
final List<Long> rolloutGroupIds = rolloutGroups.getContent().stream().map(rollout -> rollout.getId()) final List<Long> rolloutGroupIds = rolloutGroups.getContent().stream().map(RolloutGroup::getId)
.collect(Collectors.toList()); .collect(Collectors.toList());
if (rolloutGroupIds.isEmpty()) {
// groups might already deleted, so return empty list.
return new PageImpl<>(Collections.emptyList());
}
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRolloutGroup( final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRolloutGroup(
rolloutGroupIds); rolloutGroupIds);
@@ -192,8 +205,9 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
} }
@Override @Override
public Page<TargetWithActionStatus> findAllTargetsWithActionStatus(final PageRequest pageRequest, public Page<TargetWithActionStatus> findAllTargetsWithActionStatus(final Pageable pageRequest,
final Long rolloutGroupId) { final Long rolloutGroupId) {
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class); final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
@@ -218,11 +232,14 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
.setFirstResult(pageRequest.getOffset()).setMaxResults(pageRequest.getPageSize()).getResultList() .setFirstResult(pageRequest.getOffset()).setMaxResults(pageRequest.getPageSize()).getResultList()
.stream().map(o -> new TargetWithActionStatus((Target) o[0], (Action.Status) o[1])) .stream().map(o -> new TargetWithActionStatus((Target) o[0], (Action.Status) o[1]))
.collect(Collectors.toList()); .collect(Collectors.toList());
return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount); return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount);
} }
@Override @Override
public Long countTargetsOfRolloutsGroup(@NotNull final Long rolloutGroupId) { public Long countTargetsOfRolloutsGroup(@NotNull final Long rolloutGroupId) {
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Long> countQuery = cb.createQuery(Long.class); final CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
final Root<RolloutTargetGroup> countQueryFrom = countQuery.from(RolloutTargetGroup.class); final Root<RolloutTargetGroup> countQueryFrom = countQuery.from(RolloutTargetGroup.class);
@@ -231,4 +248,10 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
return entityManager.createQuery(countQuery).getSingleResult(); return entityManager.createQuery(countQuery).getSingleResult();
} }
private void throwExceptionIfRolloutGroupDoesNotExist(final Long rolloutGroupId) {
if (!rolloutGroupRepository.exists(rolloutGroupId)) {
throw new EntityNotFoundException(RolloutGroup.class, rolloutGroupId);
}
}
} }

View File

@@ -0,0 +1,106 @@
/**
* 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.jpa;
import java.util.Collections;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
/**
* A collection of static helper methods for the {@link JpaRolloutManagement}
*/
final class JpaRolloutHelper {
private JpaRolloutHelper() {
}
/**
* In case the given group is missing conditions or actions, they will be
* set from the supplied default conditions.
*
* @param create
* group to check
* @param conditions
* default conditions and actions
*/
static JpaRolloutGroup prepareRolloutGroupWithDefaultConditions(final RolloutGroupCreate create,
final RolloutGroupConditions conditions) {
final JpaRolloutGroup group = ((JpaRolloutGroupCreate) create).build();
if (group.getSuccessCondition() == null) {
group.setSuccessCondition(conditions.getSuccessCondition());
}
if (group.getSuccessConditionExp() == null) {
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
}
if (group.getSuccessAction() == null) {
group.setSuccessAction(conditions.getSuccessAction());
}
if (group.getSuccessActionExp() == null) {
group.setSuccessActionExp(conditions.getSuccessActionExp());
}
if (group.getErrorCondition() == null) {
group.setErrorCondition(conditions.getErrorCondition());
}
if (group.getErrorConditionExp() == null) {
group.setErrorConditionExp(conditions.getErrorConditionExp());
}
if (group.getErrorAction() == null) {
group.setErrorAction(conditions.getErrorAction());
}
if (group.getErrorActionExp() == null) {
group.setErrorActionExp(conditions.getErrorActionExp());
}
return group;
}
/**
* Builds a {@link Specification} to search a rollout by name or
* description.
*
* @param searchText
* search string
* @param isDeleted
* <code>true</code> if deleted rollouts should be included in
* the search. Otherwise <code>false</code>
* @return criteria specification with a query for name or description of a
* rollout
*/
static Specification<JpaRollout> likeNameOrDescription(final String searchText, final boolean isDeleted) {
return (rolloutRoot, query, criteriaBuilder) -> {
final String searchTextToLower = searchText.toLowerCase();
return criteriaBuilder.and(criteriaBuilder.or(
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(JpaRollout_.name)), searchTextToLower),
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(JpaRollout_.description)),
searchTextToLower)),
criteriaBuilder.equal(rolloutRoot.get(JpaRollout_.deleted), isDeleted));
};
}
static Page<Rollout> convertPage(final Page<JpaRollout> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements());
}
static Slice<Rollout> convertPage(final Slice<JpaRollout> findAll, final Pageable pageable) {
return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, 0);
}
}

View File

@@ -8,22 +8,23 @@
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.validation.ConstraintDeclarationException; import javax.validation.ConstraintDeclarationException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.AbstractRolloutManagement;
import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.RolloutFields; import org.eclipse.hawkbit.repository.RolloutFields;
import org.eclipse.hawkbit.repository.RolloutGroupManagement; import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutHelper;
import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.builder.GenericRolloutUpdate; import org.eclipse.hawkbit.repository.builder.GenericRolloutUpdate;
@@ -31,6 +32,7 @@ import org.eclipse.hawkbit.repository.builder.RolloutCreate;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate; import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
import org.eclipse.hawkbit.repository.builder.RolloutUpdate; import org.eclipse.hawkbit.repository.builder.RolloutUpdate;
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.ConstraintViolationException; import org.eclipse.hawkbit.repository.exception.ConstraintViolationException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -43,6 +45,8 @@ import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupActionEvaluator; import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupActionEvaluator;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupConditionEvaluator; import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupConditionEvaluator;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -58,7 +62,9 @@ import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus; import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus; import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
@@ -71,87 +77,87 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice; import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionException;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import com.google.common.collect.Lists;
/** /**
* JPA implementation of {@link RolloutManagement}. * JPA implementation of {@link RolloutManagement}.
*/ */
@Validated @Validated
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public class JpaRolloutManagement implements RolloutManagement { public class JpaRolloutManagement extends AbstractRolloutManagement {
private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class); private static final Logger LOGGER = LoggerFactory.getLogger(JpaRolloutManagement.class);
/** /**
* Max amount of targets that are handled in one transaction. * Max amount of targets that are handled in one transaction.
*/ */
private static final int TRANSACTION_TARGETS = 1000; private static final int TRANSACTION_TARGETS = 5_000;
/**
* Maximum amount of actions that are deleted in one transaction.
*/
private static final int TRANSACTION_ACTIONS = 5_000;
@Autowired @Autowired
private RolloutRepository rolloutRepository; private RolloutRepository rolloutRepository;
@Autowired
private TargetManagement targetManagement;
@Autowired @Autowired
private RolloutGroupRepository rolloutGroupRepository; private RolloutGroupRepository rolloutGroupRepository;
@Autowired
private DeploymentManagement deploymentManagement;
@Autowired @Autowired
private RolloutTargetGroupRepository rolloutTargetGroupRepository; private RolloutTargetGroupRepository rolloutTargetGroupRepository;
@Autowired
private RolloutGroupManagement rolloutGroupManagement;
@Autowired
private DistributionSetManagement distributionSetManagement;
@Autowired @Autowired
private ActionRepository actionRepository; private ActionRepository actionRepository;
@Autowired
private ApplicationContext context;
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private NoCountPagingRepository criteriaNoCountDao;
@Autowired
private PlatformTransactionManager txManager;
@Autowired
private VirtualPropertyReplacer virtualPropertyReplacer;
@Autowired @Autowired
private AfterTransactionCommitExecutor afterCommit; private AfterTransactionCommitExecutor afterCommit;
@Override JpaRolloutManagement(final TargetManagement targetManagement, final DeploymentManagement deploymentManagement,
public Page<Rollout> findAll(final Pageable pageable) { final RolloutGroupManagement rolloutGroupManagement,
return RolloutHelper.convertPage(rolloutRepository.findAll(pageable), pageable); final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
final ApplicationEventPublisher eventPublisher, final VirtualPropertyReplacer virtualPropertyReplacer,
final PlatformTransactionManager txManager, final TenantAware tenantAware,
final LockRegistry lockRegistry) {
super(targetManagement, deploymentManagement, rolloutGroupManagement, distributionSetManagement, context,
eventPublisher, virtualPropertyReplacer, txManager, tenantAware, lockRegistry);
} }
@Override @Override
public Page<Rollout> findAllByPredicate(final String rsqlParam, final Pageable pageable) { public Page<Rollout> findAll(final Pageable pageable, final boolean deleted) {
final Specification<JpaRollout> specification = RSQLUtility.parse(rsqlParam, RolloutFields.class, final Specification<JpaRollout> spec = RolloutSpecification.isDeleted(deleted);
virtualPropertyReplacer); return JpaRolloutHelper.convertPage(rolloutRepository.findAll(spec, pageable), pageable);
}
final Page<JpaRollout> findAll = rolloutRepository.findAll(specification, pageable); @Override
return RolloutHelper.convertPage(findAll, pageable); public Page<Rollout> findAllByPredicate(final String rsqlParam, final Pageable pageable, final boolean deleted) {
final List<Specification<JpaRollout>> specList = Lists.newArrayListWithExpectedSize(2);
specList.add(RSQLUtility.parse(rsqlParam, RolloutFields.class, virtualPropertyReplacer));
specList.add(RolloutSpecification.isDeleted(deleted));
return JpaRolloutHelper.convertPage(findByCriteriaAPI(pageable, specList), pageable);
}
/**
* Executes findAll with the given {@link Rollout} {@link Specification}s.
*/
private Page<JpaRollout> findByCriteriaAPI(final Pageable pageable,
final List<Specification<JpaRollout>> specList) {
if (specList == null || specList.isEmpty()) {
return rolloutRepository.findAll(pageable);
}
return rolloutRepository.findAll(SpecificationsBuilder.combineWithAnd(specList), pageable);
} }
@Override @Override
@@ -225,8 +231,7 @@ public class JpaRolloutManagement implements RolloutManagement {
group.setTargetPercentage(1.0F / (amountOfGroups - i) * 100); group.setTargetPercentage(1.0F / (amountOfGroups - i) * 100);
lastSavedGroup = rolloutGroupRepository.save(group); lastSavedGroup = rolloutGroupRepository.save(group);
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup); publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
} }
savedRollout.setRolloutGroupsCreated(amountOfGroups); savedRollout.setRolloutGroupsCreated(amountOfGroups);
@@ -240,7 +245,7 @@ public class JpaRolloutManagement implements RolloutManagement {
// Preparing the groups // Preparing the groups
final List<RolloutGroup> groups = groupList.stream() final List<RolloutGroup> groups = groupList.stream()
.map(group -> RolloutHelper.prepareRolloutGroupWithDefaultConditions(group, conditions)) .map(group -> JpaRolloutHelper.prepareRolloutGroupWithDefaultConditions(group, conditions))
.collect(Collectors.toList()); .collect(Collectors.toList());
groups.forEach(RolloutHelper::verifyRolloutGroupHasConditions); groups.forEach(RolloutHelper::verifyRolloutGroupHasConditions);
@@ -261,7 +266,7 @@ public class JpaRolloutManagement implements RolloutManagement {
if (srcGroup.getTargetFilterQuery() != null) { if (srcGroup.getTargetFilterQuery() != null) {
group.setTargetFilterQuery(srcGroup.getTargetFilterQuery()); group.setTargetFilterQuery(srcGroup.getTargetFilterQuery());
} else { } else {
group.setTargetFilterQuery(""); group.setTargetFilterQuery(StringUtils.EMPTY);
} }
group.setSuccessCondition(srcGroup.getSuccessCondition()); group.setSuccessCondition(srcGroup.getSuccessCondition());
@@ -277,25 +282,20 @@ public class JpaRolloutManagement implements RolloutManagement {
group.setErrorActionExp(srcGroup.getErrorActionExp()); group.setErrorActionExp(srcGroup.getErrorActionExp());
lastSavedGroup = rolloutGroupRepository.save(group); lastSavedGroup = rolloutGroupRepository.save(group);
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup); publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
} }
savedRollout.setRolloutGroupsCreated(groups.size()); savedRollout.setRolloutGroupsCreated(groups.size());
return rolloutRepository.save(savedRollout); return rolloutRepository.save(savedRollout);
} }
private void publishRolloutGroupCreatedEventAfterCommit(final RolloutGroup group) { private void publishRolloutGroupCreatedEventAfterCommit(final RolloutGroup group, final Rollout rollout) {
afterCommit afterCommit.afterCommit(() -> eventPublisher
.afterCommit(() -> eventPublisher.publishEvent(new RolloutGroupCreatedEvent(group, context.getId()))); .publishEvent(new RolloutGroupCreatedEvent(group, rollout.getId(), context.getId())));
} }
@Override private void handleCreateRollout(final JpaRollout rollout) {
@Transactional(isolation = Isolation.READ_UNCOMMITTED) LOGGER.debug("handleCreateRollout called for rollout {}", rollout.getId());
@Modifying
public void fillRolloutGroupsWithTargets(final Long rolloutId) {
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.CREATING);
final List<RolloutGroup> rolloutGroups = RolloutHelper.getOrderedGroups(rollout); final List<RolloutGroup> rolloutGroups = RolloutHelper.getOrderedGroups(rollout);
int readyGroups = 0; int readyGroups = 0;
@@ -317,6 +317,7 @@ public class JpaRolloutManagement implements RolloutManagement {
// When all groups are ready the rollout status can be changed to be // When all groups are ready the rollout status can be changed to be
// ready, too. // ready, too.
if (readyGroups == rolloutGroups.size()) { if (readyGroups == rolloutGroups.size()) {
LOGGER.debug("rollout {} creatin done. Switch to READY.", rollout.getId());
rollout.setStatus(RolloutStatus.READY); rollout.setStatus(RolloutStatus.READY);
rollout.setLastCheck(0); rollout.setLastCheck(0);
rollout.setTotalTargets(totalTargets); rollout.setTotalTargets(totalTargets);
@@ -372,17 +373,10 @@ public class JpaRolloutManagement implements RolloutManagement {
} }
} }
private int runInNewCountingTransaction(final String transactionName, final TransactionCallback<Integer> action) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName(transactionName);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
return new TransactionTemplate(txManager, def).execute(action);
}
private Integer assignTargetsToGroupInNewTransaction(final Rollout rollout, final RolloutGroup group, private Integer assignTargetsToGroupInNewTransaction(final Rollout rollout, final RolloutGroup group,
final String targetFilter, final long limit) { final String targetFilter, final long limit) {
return runInNewCountingTransaction("assignTargetsToRolloutGroup", status -> { return runInNewTransaction("assignTargetsToRolloutGroup", status -> {
final PageRequest pageRequest = new PageRequest(0, Math.toIntExact(limit)); final PageRequest pageRequest = new PageRequest(0, Math.toIntExact(limit));
final List<Long> readyGroups = RolloutHelper.getGroupsByStatusIncludingGroup(rollout, final List<Long> readyGroups = RolloutHelper.getGroupsByStatusIncludingGroup(rollout,
RolloutGroupStatus.READY, group); RolloutGroupStatus.READY, group);
@@ -399,19 +393,6 @@ public class JpaRolloutManagement implements RolloutManagement {
targets.forEach(target -> rolloutTargetGroupRepository.save(new RolloutTargetGroup(group, target))); targets.forEach(target -> rolloutTargetGroupRepository.save(new RolloutTargetGroup(group, target)));
} }
private long calculateRemainingTargets(final List<RolloutGroup> groups, final String targetFilter,
final Long createdAt) {
final String baseFilter = RolloutHelper.getTargetFilterQuery(targetFilter, createdAt);
final long totalTargets = targetManagement.countTargetByTargetFilterQuery(baseFilter);
if (totalTargets == 0) {
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
}
final RolloutGroupsValidation validation = validateTargetsInGroups(groups, baseFilter, totalTargets);
return totalTargets - validation.getTargetsInGroups();
}
@Override @Override
@Async @Async
public ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(final List<RolloutGroupCreate> groups, public ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(final List<RolloutGroupCreate> groups,
@@ -427,67 +408,12 @@ public class JpaRolloutManagement implements RolloutManagement {
groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()), baseFilter, totalTargets)); groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()), baseFilter, totalTargets));
} }
private RolloutGroupsValidation validateTargetsInGroups(final List<RolloutGroup> groups, final String baseFilter,
final long totalTargets) {
final List<Long> groupTargetCounts = new ArrayList<>(groups.size());
final Map<String, Long> targetFilterCounts = groups.stream()
.map(group -> RolloutHelper.getGroupTargetFilter(baseFilter, group)).distinct()
.collect(Collectors.toMap(Function.identity(), targetManagement::countTargetByTargetFilterQuery));
long unusedTargetsCount = 0;
for (int i = 0; i < groups.size(); i++) {
final RolloutGroup group = groups.get(i);
final String groupTargetFilter = RolloutHelper.getGroupTargetFilter(baseFilter, group);
RolloutHelper.verifyRolloutGroupTargetPercentage(group.getTargetPercentage());
final long targetsInGroupFilter = targetFilterCounts.get(groupTargetFilter);
final long overlappingTargets = countOverlappingTargetsWithPreviousGroups(baseFilter, groups, group, i,
targetFilterCounts);
final long realTargetsInGroup;
// Assume that targets which were not used in the previous groups
// are used in this group
if (overlappingTargets > 0 && unusedTargetsCount > 0) {
realTargetsInGroup = targetsInGroupFilter - overlappingTargets + unusedTargetsCount;
unusedTargetsCount = 0;
} else {
realTargetsInGroup = targetsInGroupFilter - overlappingTargets;
}
final long reducedTargetsInGroup = Math
.round(group.getTargetPercentage() / 100 * (double) realTargetsInGroup);
groupTargetCounts.add(reducedTargetsInGroup);
unusedTargetsCount += realTargetsInGroup - reducedTargetsInGroup;
}
return new RolloutGroupsValidation(totalTargets, groupTargetCounts);
}
private long countOverlappingTargetsWithPreviousGroups(final String baseFilter, final List<RolloutGroup> groups,
final RolloutGroup group, final int groupIndex, final Map<String, Long> targetFilterCounts) {
// there can't be overlapping targets in the first group
if (groupIndex == 0) {
return 0;
}
final List<RolloutGroup> previousGroups = groups.subList(0, groupIndex);
final String overlappingTargetsFilter = RolloutHelper.getOverlappingWithGroupsTargetFilter(baseFilter,
previousGroups, group);
if (targetFilterCounts.containsKey(overlappingTargetsFilter)) {
return targetFilterCounts.get(overlappingTargetsFilter);
} else {
final long overlappingTargets = targetManagement.countTargetByTargetFilterQuery(overlappingTargetsFilter);
targetFilterCounts.put(overlappingTargetsFilter, overlappingTargets);
return overlappingTargets;
}
}
@Override @Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED) @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
public Rollout startRollout(final Long rolloutId) { public Rollout startRollout(final Long rolloutId) {
LOGGER.debug("startRollout called for rollout {}", rolloutId);
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId); final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
RolloutHelper.checkIfRolloutCanStarted(rollout, rollout); RolloutHelper.checkIfRolloutCanStarted(rollout, rollout);
rollout.setStatus(RolloutStatus.STARTING); rollout.setStatus(RolloutStatus.STARTING);
@@ -496,6 +422,7 @@ public class JpaRolloutManagement implements RolloutManagement {
} }
private void startFirstRolloutGroup(final Rollout rollout) { private void startFirstRolloutGroup(final Rollout rollout) {
LOGGER.debug("startFirstRolloutGroup called for rollout {}", rollout.getId());
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.STARTING); RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.STARTING);
final JpaRollout jpaRollout = (JpaRollout) rollout; final JpaRollout jpaRollout = (JpaRollout) rollout;
@@ -516,7 +443,6 @@ public class JpaRolloutManagement implements RolloutManagement {
} }
private boolean ensureAllGroupsAreScheduled(final Rollout rollout) { private boolean ensureAllGroupsAreScheduled(final Rollout rollout) {
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.STARTING);
final JpaRollout jpaRollout = (JpaRollout) rollout; final JpaRollout jpaRollout = (JpaRollout) rollout;
final List<JpaRolloutGroup> groupsToBeScheduled = rolloutGroupRepository.findByRolloutAndStatus(rollout, final List<JpaRolloutGroup> groupsToBeScheduled = rolloutGroupRepository.findByRolloutAndStatus(rollout,
@@ -566,7 +492,7 @@ public class JpaRolloutManagement implements RolloutManagement {
} }
private Integer createActionsForTargetsInNewTransaction(final long rolloutId, final long groupId, final int limit) { private Integer createActionsForTargetsInNewTransaction(final long rolloutId, final long groupId, final int limit) {
return runInNewCountingTransaction("createActionsForTargets", status -> { return runInNewTransaction("createActionsForTargets", status -> {
final PageRequest pageRequest = new PageRequest(0, limit); final PageRequest pageRequest = new PageRequest(0, limit);
final Rollout rollout = rolloutRepository.findOne(rolloutId); final Rollout rollout = rolloutRepository.findOne(rolloutId);
final RolloutGroup group = rolloutGroupRepository.findOne(groupId); final RolloutGroup group = rolloutGroupRepository.findOne(groupId);
@@ -618,7 +544,7 @@ public class JpaRolloutManagement implements RolloutManagement {
@Modifying @Modifying
public void pauseRollout(final Long rolloutId) { public void pauseRollout(final Long rolloutId) {
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId); final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
if (rollout.getStatus() != RolloutStatus.RUNNING) { if (!RolloutStatus.RUNNING.equals(rollout.getStatus())) {
throw new RolloutIllegalStateException("Rollout can only be paused in state running but current state is " throw new RolloutIllegalStateException("Rollout can only be paused in state running but current state is "
+ rollout.getStatus().name().toLowerCase()); + rollout.getStatus().name().toLowerCase());
} }
@@ -644,39 +570,27 @@ public class JpaRolloutManagement implements RolloutManagement {
rolloutRepository.save(rollout); rolloutRepository.save(rollout);
} }
@Override private void handleRunningRollout(final JpaRollout rollout) {
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED) LOGGER.debug("handleRunningRollout called for rollout {}", rollout.getId());
@Modifying
public void checkRunningRollouts(final long delayBetweenChecks) { final List<JpaRolloutGroup> rolloutGroupsRunning = rolloutGroupRepository.findByRolloutAndStatus(rollout,
final List<JpaRollout> rolloutsToCheck = getRolloutsToCheckForStatus(delayBetweenChecks, RolloutStatus.RUNNING); RolloutGroupStatus.RUNNING);
if (rolloutsToCheck.isEmpty()) {
return; if (rolloutGroupsRunning.isEmpty()) {
// no running rollouts, probably there was an error
// somewhere at the latest group. And the latest group has
// been switched from running into error state. So we need
// to find the latest group which
executeLatestRolloutGroup(rollout);
} else {
LOGGER.debug("Rollout {} has {} running groups", rollout.getId(), rolloutGroupsRunning.size());
executeRolloutGroups(rollout, rolloutGroupsRunning);
} }
LOGGER.info("Found {} running rollouts to check", rolloutsToCheck.size()); if (isRolloutComplete(rollout)) {
LOGGER.info("Rollout {} is finished, setting FINISHED status", rollout);
for (final JpaRollout rollout : rolloutsToCheck) { rollout.setStatus(RolloutStatus.FINISHED);
LOGGER.debug("Checking rollout {}", rollout); rolloutRepository.save(rollout);
final List<JpaRolloutGroup> rolloutGroupsRunning = rolloutGroupRepository.findByRolloutAndStatus(rollout,
RolloutGroupStatus.RUNNING);
if (rolloutGroupsRunning.isEmpty()) {
// no running rollouts, probably there was an error
// somewhere at the latest group. And the latest group has
// been switched from running into error state. So we need
// to find the latest group which
executeLatestRolloutGroup(rollout);
} else {
LOGGER.debug("Rollout {} has {} running groups", rollout.getId(), rolloutGroupsRunning.size());
executeRolloutGroups(rollout, rolloutGroupsRunning);
}
if (isRolloutComplete(rollout)) {
LOGGER.info("Rollout {} is finished, setting finished status", rollout);
rollout.setStatus(RolloutStatus.FINISHED);
rolloutRepository.save(rollout);
}
} }
} }
@@ -741,7 +655,7 @@ public class JpaRolloutManagement implements RolloutManagement {
} }
private boolean isRolloutComplete(final JpaRollout rollout) { private boolean isRolloutComplete(final JpaRollout rollout) {
final Long groupsActiveLeft = rolloutGroupRepository.countByRolloutAndStatusOrStatus(rollout, final Long groupsActiveLeft = rolloutGroupRepository.countByRolloutIdAndStatusOrStatus(rollout.getId(),
RolloutGroupStatus.RUNNING, RolloutGroupStatus.SCHEDULED); RolloutGroupStatus.RUNNING, RolloutGroupStatus.SCHEDULED);
return groupsActiveLeft == 0; return groupsActiveLeft == 0;
} }
@@ -798,90 +712,170 @@ public class JpaRolloutManagement implements RolloutManagement {
} }
@Override @Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED) // No transaction, will be created per handled rollout
@Modifying @Transactional(propagation = Propagation.NEVER)
public void checkCreatingRollouts(final long delayBetweenChecks) { public void handleRollouts() {
final List<Long> rolloutsToCheck = getRolloutsToCheckForStatus(delayBetweenChecks, RolloutStatus.CREATING) rolloutRepository
.stream().map(Rollout::getId).collect(Collectors.toList()); .findByStatusIn(Lists.newArrayList(RolloutStatus.CREATING, RolloutStatus.DELETING,
if (rolloutsToCheck.isEmpty()) { RolloutStatus.STARTING, RolloutStatus.READY, RolloutStatus.RUNNING))
.forEach(this::handleRollout);
}
private void handleRollout(final Long rolloutId) {
LOGGER.debug("handleRollout called for rollout {}", rolloutId);
final String tenant = tenantAware.getCurrentTenant();
final String handlerId = tenant + "-rollout-" + rolloutId;
final Lock lock = lockRegistry.obtain(handlerId);
if (!lock.tryLock()) {
return; return;
} }
LOGGER.info("Found {} creating rollouts to check", rolloutsToCheck.size()); try {
runInNewTransaction(handlerId, status -> executeFittingHandler(rolloutId));
} finally {
lock.unlock();
}
}
rolloutsToCheck.forEach(this::fillRolloutGroupsWithTargets); private int executeFittingHandler(final Long rolloutId) {
final JpaRollout rollout = rolloutRepository.findOne(rolloutId);
switch (rollout.getStatus()) {
case CREATING:
handleCreateRollout(rollout);
break;
case DELETING:
handleDeleteRollout(rollout);
break;
case READY:
handleReadyRollout(rollout);
break;
case STARTING:
handleStartingRollout(rollout);
break;
case RUNNING:
handleRunningRollout(rollout);
break;
default:
LOGGER.error("Rollout in status {} not supposed to be handled!", rollout.getStatus());
break;
}
return 0;
}
private void handleStartingRollout(final Rollout rollout) {
LOGGER.debug("handleStartingRollout called for rollout {}", rollout.getId());
if (ensureAllGroupsAreScheduled(rollout)) {
startFirstRolloutGroup(rollout);
}
}
private void handleReadyRollout(final Rollout rollout) {
if (rollout.getStartAt() != null && rollout.getStartAt() <= System.currentTimeMillis()) {
LOGGER.debug(
"handleReadyRollout called for rollout {} with autostart beyond define time. Switch to STARTING",
rollout.getId());
startRollout(rollout.getId());
}
} }
@Override @Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED) @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
public void checkStartingRollouts(final long delayBetweenChecks) { public void deleteRollout(final long rolloutId) {
final List<JpaRollout> rolloutsToCheck = getRolloutsToCheckForStatus(delayBetweenChecks, final JpaRollout jpaRollout = rolloutRepository.findOne(rolloutId);
RolloutStatus.STARTING); if (RolloutStatus.DELETING.equals(jpaRollout.getStatus())) {
if (rolloutsToCheck.isEmpty()) {
return; return;
} }
LOGGER.info("Found {} starting rollouts to check", rolloutsToCheck.size()); jpaRollout.setStatus(RolloutStatus.DELETING);
rolloutRepository.save(jpaRollout);
rolloutsToCheck.forEach(rollout -> {
if (ensureAllGroupsAreScheduled(rollout)) {
startFirstRolloutGroup(rollout);
}
});
} }
@Override private void handleDeleteRollout(final JpaRollout rollout) {
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED) LOGGER.debug("handleDeleteRollout called for {}", rollout.getId());
@Modifying
public void checkReadyRollouts(final long delayBetweenChecks) { // check if there are actions beyond schedule
final List<JpaRollout> rolloutsToCheck = getRolloutsToCheckForStatus(delayBetweenChecks, RolloutStatus.READY); boolean hardDeleteRolloutGroups = !actionRepository.existsByRolloutIdAndStatusNotIn(rollout.getId(),
if (rolloutsToCheck.isEmpty()) { Status.SCHEDULED);
if (hardDeleteRolloutGroups) {
LOGGER.debug("Rollout {} has no actions other than scheduled -> hard delete", rollout.getId());
hardDeleteRollout(rollout);
return;
}
// clean up all scheduled actions
final Slice<JpaAction> scheduledActions = findScheduledActionsByRollout(rollout);
deleteScheduledActions(rollout, scheduledActions);
// avoid another scheduler round and re-check if all scheduled actions
// has been cleaned up
final boolean hasScheduledActionsLeft = findScheduledActionsByRollout(rollout).getNumberOfElements() > 0;
if (hasScheduledActionsLeft) {
return; return;
} }
LOGGER.info("Found {} ready rollouts to check", rolloutsToCheck.size()); // only hard delete the rollout if no actions are left for the rollout.
// In case actions are left, they are probably are running or were
final long now = System.currentTimeMillis(); // running before, so only soft delete.
hardDeleteRolloutGroups = !actionRepository.existsByRolloutId(rollout.getId());
rolloutsToCheck.forEach(rollout -> { if (hardDeleteRolloutGroups) {
if (rollout.getStartAt() != null && rollout.getStartAt() <= now) { hardDeleteRollout(rollout);
startRollout(rollout.getId()); return;
}
});
}
private List<JpaRollout> getRolloutsToCheckForStatus(final long delayBetweenChecks, final RolloutStatus status) {
final long lastCheck = System.currentTimeMillis();
final int updated = rolloutRepository.updateLastCheck(lastCheck, delayBetweenChecks, status);
if (updated == 0) {
// nothing to check, maybe another instance already checked in
// between
LOGGER.debug("No rollouts starting check necessary for current scheduled check {}, next check at {}",
lastCheck, lastCheck + delayBetweenChecks);
return Collections.emptyList();
} }
return rolloutRepository.findByLastCheckAndStatus(lastCheck, status); // set soft delete
rollout.setStatus(RolloutStatus.DELETED);
rollout.setDeleted(true);
rolloutRepository.save(rollout);
}
private void hardDeleteRollout(final JpaRollout rollout) {
rolloutRepository.delete(rollout);
}
private void deleteScheduledActions(final JpaRollout rollout, final Slice<JpaAction> scheduledActions) {
final boolean hasScheduledActions = scheduledActions.getNumberOfElements() > 0;
if (hasScheduledActions) {
try {
final Iterable<JpaAction> iterable = scheduledActions::iterator;
final List<Long> actionIds = StreamSupport.stream(iterable.spliterator(), false).map(Action::getId)
.collect(Collectors.toList());
actionRepository.deleteByIdIn(actionIds);
afterCommit.afterCommit(() -> eventPublisher.publishEvent(
new RolloutUpdatedEvent(rollout, EventPublisherHolder.getInstance().getApplicationId())));
} catch (final RuntimeException e) {
LOGGER.error("Exception during deletion of actions of rollout {}", rollout, e);
}
}
}
private Slice<JpaAction> findScheduledActionsByRollout(final JpaRollout rollout) {
return actionRepository.findByRolloutIdAndStatus(new PageRequest(0, TRANSACTION_ACTIONS), rollout.getId(),
Status.SCHEDULED);
} }
@Override @Override
public Long countRolloutsAll() { public Long countRolloutsAll() {
return rolloutRepository.count(); return rolloutRepository.count(RolloutSpecification.isDeleted(false));
} }
@Override @Override
public Long countRolloutsAllByFilters(final String searchText) { public Long countRolloutsAllByFilters(final String searchText) {
return rolloutRepository.count(RolloutHelper.likeNameOrDescription(searchText)); return rolloutRepository.count(JpaRolloutHelper.likeNameOrDescription(searchText, false));
} }
@Override @Override
public Slice<Rollout> findRolloutWithDetailedStatusByFilters(final Pageable pageable, final String searchText) { public Slice<Rollout> findRolloutWithDetailedStatusByFilters(final Pageable pageable, final String searchText,
final Specification<JpaRollout> specs = RolloutHelper.likeNameOrDescription(searchText); final boolean deleted) {
final Slice<JpaRollout> findAll = criteriaNoCountDao.findAll(specs, pageable, JpaRollout.class); final Slice<JpaRollout> findAll = findByCriteriaAPI(pageable,
Lists.newArrayList(JpaRolloutHelper.likeNameOrDescription(searchText, deleted)));
setRolloutStatusDetails(findAll); setRolloutStatusDetails(findAll);
return RolloutHelper.convertPage(findAll, pageable); return JpaRolloutHelper.convertPage(findAll, pageable);
} }
@Override @Override
@@ -917,10 +911,12 @@ public class JpaRolloutManagement implements RolloutManagement {
} }
@Override @Override
public Page<Rollout> findAllRolloutsWithDetailedStatus(final Pageable pageable) { public Page<Rollout> findAllRolloutsWithDetailedStatus(final Pageable pageable, final boolean deleted) {
final Page<JpaRollout> rollouts = rolloutRepository.findAll(pageable); Page<JpaRollout> rollouts;
final Specification<JpaRollout> spec = RolloutSpecification.isDeleted(deleted);
rollouts = rolloutRepository.findAll(spec, pageable);
setRolloutStatusDetails(rollouts); setRolloutStatusDetails(rollouts);
return RolloutHelper.convertPage(rollouts, pageable); return JpaRolloutHelper.convertPage(rollouts, pageable);
} }
@Override @Override
@@ -940,8 +936,12 @@ public class JpaRolloutManagement implements RolloutManagement {
} }
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRollout(final List<Long> rolloutIds) { private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRollout(final List<Long> rolloutIds) {
final List<TotalTargetCountActionStatus> resultList = actionRepository.getStatusCountByRolloutId(rolloutIds); if (!rolloutIds.isEmpty()) {
return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId)); final List<TotalTargetCountActionStatus> resultList = actionRepository
.getStatusCountByRolloutId(rolloutIds);
return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
}
return null;
} }
private void setRolloutStatusDetails(final Slice<JpaRollout> rollouts) { private void setRolloutStatusDetails(final Slice<JpaRollout> rollouts) {
@@ -949,10 +949,12 @@ public class JpaRolloutManagement implements RolloutManagement {
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRollout( final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRollout(
rolloutIds); rolloutIds);
for (final Rollout rollout : rollouts) { if (allStatesForRollout != null) {
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus( rollouts.forEach(rollout -> {
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets()); final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
((JpaRollout) rollout).setTotalTargetCountStatus(totalTargetCountStatus); allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets());
rollout.setTotalTargetCountStatus(totalTargetCountStatus);
});
} }
} }

View File

@@ -209,8 +209,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
return softwareModuleRepository.findOneByNameAndVersionAndTypeId(name, version, typeId); return softwareModuleRepository.findOneByNameAndVersionAndTypeId(name, version, typeId);
} }
private boolean isUnassigned(final JpaSoftwareModule bsmMerged) { private boolean isUnassigned(final Long moduleId) {
return distributionSetRepository.countByModules(bsmMerged) <= 0; return distributionSetRepository.countByModulesId(moduleId) <= 0;
} }
private Slice<JpaSoftwareModule> findSwModuleByCriteriaAPI(final Pageable pageable, private Slice<JpaSoftwareModule> findSwModuleByCriteriaAPI(final Pageable pageable,
@@ -225,7 +225,7 @@ public class JpaSoftwareManagement implements SoftwareManagement {
private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) { private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) {
for (final Artifact localArtifact : swModule.getArtifacts()) { for (final Artifact localArtifact : swModule.getArtifacts()) {
artifactManagement.clearArtifactBinary(localArtifact.getId()); artifactManagement.clearArtifactBinary(localArtifact.getSha1Hash(), swModule.getId());
} }
} }
@@ -246,12 +246,9 @@ public class JpaSoftwareManagement implements SoftwareManagement {
// delete binary data of artifacts // delete binary data of artifacts
deleteGridFsArtifacts(swModule); deleteGridFsArtifacts(swModule);
if (isUnassigned(swModule)) { if (isUnassigned(swModule.getId())) {
softwareModuleRepository.delete(swModule.getId());
softwareModuleRepository.delete(swModule);
} else { } else {
assignedModuleIds.add(swModule.getId()); assignedModuleIds.add(swModule.getId());
} }
}); });

View File

@@ -218,8 +218,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant); tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant);
targetRepository.deleteByTenantIgnoreCase(tenant); targetRepository.deleteByTenantIgnoreCase(tenant);
targetFilterQueryRepository.deleteByTenantIgnoreCase(tenant); targetFilterQueryRepository.deleteByTenantIgnoreCase(tenant);
actionRepository.deleteByTenantIgnoreCase(tenant);
rolloutGroupRepository.deleteByTenantIgnoreCase(tenant);
rolloutRepository.deleteByTenantIgnoreCase(tenant); rolloutRepository.deleteByTenantIgnoreCase(tenant);
artifactRepository.deleteByTenantIgnoreCase(tenant); artifactRepository.deleteByTenantIgnoreCase(tenant);
targetTagRepository.deleteByTenantIgnoreCase(tenant); targetTagRepository.deleteByTenantIgnoreCase(tenant);

View File

@@ -607,7 +607,7 @@ public class JpaTargetManagement implements TargetManagement {
final JpaTarget target = create.build(); final JpaTarget target = create.build();
if (targetRepository.findByControllerId(target.getControllerId()).isPresent()) { if (targetRepository.existsByControllerId(target.getControllerId())) {
throw new EntityAlreadyExistsException(); throw new EntityAlreadyExistsException();
} }

View File

@@ -13,9 +13,11 @@ import java.util.Optional;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact; import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -54,6 +56,21 @@ public interface LocalArtifactRepository extends BaseEntityRepository<JpaArtifac
*/ */
List<Artifact> findBySha1Hash(String sha1Hash); List<Artifact> findBySha1Hash(String sha1Hash);
/**
* Verifies if an artifact exists that has given hash and is still related
* to a {@link SoftwareModule} other than a given one and not
* {@link SoftwareModule#isDeleted()}.
*
* @param sha1
* to search for
* @param moduleId
* to ignore in relationship check
*
* @return <code>true</code> if such an artifact exists
*/
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaArtifact a WHERE a.sha1Hash = :sha1 AND a.softwareModule.id != :moduleId AND a.softwareModule.deleted = 0")
boolean existsWithSha1HashAndSoftwareModuleIdIsNot(@Param("sha1") String sha1, @Param("moduleId") Long moduleId);
/** /**
* Searches for a {@link Artifact} based on given gridFsFileName. * Searches for a {@link Artifact} based on given gridFsFileName.
* *

View File

@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.sql.DataSource; import javax.sql.DataSource;
@@ -23,7 +24,6 @@ import org.eclipse.hawkbit.repository.ReportManagement;
import org.eclipse.hawkbit.repository.RepositoryProperties; import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.RolloutGroupManagement; import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutProperties;
import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TagManagement; import org.eclipse.hawkbit.repository.TagManagement;
@@ -53,6 +53,7 @@ import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
import org.eclipse.hawkbit.repository.jpa.rollout.RolloutScheduler;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParserValidationOracle; import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParserValidationOracle;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
@@ -69,18 +70,23 @@ import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityScan; import org.springframework.boot.orm.jpa.EntityScan;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter; import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
@@ -101,7 +107,7 @@ import com.google.common.collect.Maps;
@EnableAspectJAutoProxy @EnableAspectJAutoProxy
@Configuration @Configuration
@ComponentScan @ComponentScan
@EnableConfigurationProperties({ RepositoryProperties.class, ControllerPollProperties.class, RolloutProperties.class, @EnableConfigurationProperties({ RepositoryProperties.class, ControllerPollProperties.class,
TenantConfigurationProperties.class }) TenantConfigurationProperties.class })
@EnableScheduling @EnableScheduling
@EntityScan("org.eclipse.hawkbit.repository.jpa.model") @EntityScan("org.eclipse.hawkbit.repository.jpa.model")
@@ -115,7 +121,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public RsqlValidationOracle rsqlValidationOracle() { RsqlValidationOracle rsqlValidationOracle() {
return new RsqlParserValidationOracle(); return new RsqlParserValidationOracle();
} }
@@ -127,7 +133,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return DistributionSetBuilder bean * @return DistributionSetBuilder bean
*/ */
@Bean @Bean
public DistributionSetBuilder distributionSetBuilder(final DistributionSetManagement distributionSetManagement, DistributionSetBuilder distributionSetBuilder(final DistributionSetManagement distributionSetManagement,
final SoftwareManagement softwareManagement) { final SoftwareManagement softwareManagement) {
return new JpaDistributionSetBuilder(distributionSetManagement, softwareManagement); return new JpaDistributionSetBuilder(distributionSetManagement, softwareManagement);
} }
@@ -140,7 +146,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return DistributionSetTypeBuilder bean * @return DistributionSetTypeBuilder bean
*/ */
@Bean @Bean
public DistributionSetTypeBuilder distributionSetTypeBuilder(final SoftwareManagement softwareManagement) { DistributionSetTypeBuilder distributionSetTypeBuilder(final SoftwareManagement softwareManagement) {
return new JpaDistributionSetTypeBuilder(softwareManagement); return new JpaDistributionSetTypeBuilder(softwareManagement);
} }
@@ -150,7 +156,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return SoftwareModuleBuilder bean * @return SoftwareModuleBuilder bean
*/ */
@Bean @Bean
public SoftwareModuleBuilder softwareModuleBuilder(final SoftwareManagement softwareManagement) { SoftwareModuleBuilder softwareModuleBuilder(final SoftwareManagement softwareManagement) {
return new JpaSoftwareModuleBuilder(softwareManagement); return new JpaSoftwareModuleBuilder(softwareManagement);
} }
@@ -160,7 +166,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return RolloutBuilder bean * @return RolloutBuilder bean
*/ */
@Bean @Bean
public RolloutBuilder rolloutBuilder(final DistributionSetManagement distributionSetManagement) { RolloutBuilder rolloutBuilder(final DistributionSetManagement distributionSetManagement) {
return new JpaRolloutBuilder(distributionSetManagement); return new JpaRolloutBuilder(distributionSetManagement);
} }
@@ -171,8 +177,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return TargetFilterQueryBuilder bean * @return TargetFilterQueryBuilder bean
*/ */
@Bean @Bean
public TargetFilterQueryBuilder targetFilterQueryBuilder( TargetFilterQueryBuilder targetFilterQueryBuilder(final DistributionSetManagement distributionSetManagement) {
final DistributionSetManagement distributionSetManagement) {
return new JpaTargetFilterQueryBuilder(distributionSetManagement); return new JpaTargetFilterQueryBuilder(distributionSetManagement);
} }
@@ -182,7 +187,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* e.g. JPA entities. * e.g. JPA entities.
*/ */
@Bean @Bean
public SystemSecurityContextHolder systemSecurityContextHolder() { SystemSecurityContextHolder systemSecurityContextHolder() {
return SystemSecurityContextHolder.getInstance(); return SystemSecurityContextHolder.getInstance();
} }
@@ -192,7 +197,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* directly, e.g. JPA entities. * directly, e.g. JPA entities.
*/ */
@Bean @Bean
public TenantConfigurationManagementHolder tenantConfigurationManagementHolder() { TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
return TenantConfigurationManagementHolder.getInstance(); return TenantConfigurationManagementHolder.getInstance();
} }
@@ -203,7 +208,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* entities. * entities.
*/ */
@Bean @Bean
public SystemManagementHolder systemManagementHolder() { SystemManagementHolder systemManagementHolder() {
return SystemManagementHolder.getInstance(); return SystemManagementHolder.getInstance();
} }
@@ -214,7 +219,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* entities. * entities.
*/ */
@Bean @Bean
public TenantAwareHolder tenantAwareHolder() { TenantAwareHolder tenantAwareHolder() {
return TenantAwareHolder.getInstance(); return TenantAwareHolder.getInstance();
} }
@@ -225,7 +230,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* injection * injection
*/ */
@Bean @Bean
public SecurityTokenGeneratorHolder securityTokenGeneratorHolder() { SecurityTokenGeneratorHolder securityTokenGeneratorHolder() {
return SecurityTokenGeneratorHolder.getInstance(); return SecurityTokenGeneratorHolder.getInstance();
} }
@@ -233,7 +238,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return the singleton instance of the {@link EntityInterceptorHolder} * @return the singleton instance of the {@link EntityInterceptorHolder}
*/ */
@Bean @Bean
public EntityInterceptorHolder entityInterceptorHolder() { EntityInterceptorHolder entityInterceptorHolder() {
return EntityInterceptorHolder.getInstance(); return EntityInterceptorHolder.getInstance();
} }
@@ -243,7 +248,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* {@link AfterTransactionCommitExecutorHolder} * {@link AfterTransactionCommitExecutorHolder}
*/ */
@Bean @Bean
public AfterTransactionCommitExecutorHolder afterTransactionCommitExecutorHolder() { AfterTransactionCommitExecutorHolder afterTransactionCommitExecutorHolder() {
return AfterTransactionCommitExecutorHolder.getInstance(); return AfterTransactionCommitExecutorHolder.getInstance();
} }
@@ -261,7 +266,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* @return {@link ExceptionMappingAspectHandler} aspect bean * @return {@link ExceptionMappingAspectHandler} aspect bean
*/ */
@Bean @Bean
public ExceptionMappingAspectHandler createRepositoryExceptionHandlerAdvice() { ExceptionMappingAspectHandler createRepositoryExceptionHandlerAdvice() {
return new ExceptionMappingAspectHandler(); return new ExceptionMappingAspectHandler();
} }
@@ -305,7 +310,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public SystemManagement systemManagement() { SystemManagement systemManagement() {
return new JpaSystemManagement(); return new JpaSystemManagement();
} }
@@ -316,7 +321,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public ReportManagement reportManagement() { ReportManagement reportManagement() {
return new JpaReportManagement(); return new JpaReportManagement();
} }
@@ -327,7 +332,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public DistributionSetManagement distributionSetManagement() { DistributionSetManagement distributionSetManagement() {
return new JpaDistributionSetManagement(); return new JpaDistributionSetManagement();
} }
@@ -338,7 +343,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public TenantStatsManagement tenantStatsManagement() { TenantStatsManagement tenantStatsManagement() {
return new JpaTenantStatsManagement(); return new JpaTenantStatsManagement();
} }
@@ -349,7 +354,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public TenantConfigurationManagement tenantConfigurationManagement() { TenantConfigurationManagement tenantConfigurationManagement() {
return new JpaTenantConfigurationManagement(); return new JpaTenantConfigurationManagement();
} }
@@ -360,7 +365,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public TargetManagement targetManagement() { TargetManagement targetManagement() {
return new JpaTargetManagement(); return new JpaTargetManagement();
} }
@@ -378,7 +383,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public TargetFilterQueryManagement targetFilterQueryManagement( TargetFilterQueryManagement targetFilterQueryManagement(
final TargetFilterQueryRepository targetFilterQueryRepository, final TargetFilterQueryRepository targetFilterQueryRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final VirtualPropertyReplacer virtualPropertyReplacer,
final DistributionSetManagement distributionSetManagement) { final DistributionSetManagement distributionSetManagement) {
@@ -393,7 +398,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public TagManagement tagManagement() { TagManagement tagManagement() {
return new JpaTagManagement(); return new JpaTagManagement();
} }
@@ -404,19 +409,21 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public SoftwareManagement softwareManagement() { SoftwareManagement softwareManagement() {
return new JpaSoftwareManagement(); return new JpaSoftwareManagement();
} }
/**
* {@link JpaRolloutManagement} bean.
*
* @return a new {@link RolloutManagement}
*/
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public RolloutManagement rolloutManagement() { RolloutManagement rolloutManagement(final TargetManagement targetManagement,
return new JpaRolloutManagement(); final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
final ApplicationEventPublisher eventPublisher, final VirtualPropertyReplacer virtualPropertyReplacer,
final PlatformTransactionManager txManager, final TenantAware tenantAware,
final LockRegistry lockRegistry) {
return new JpaRolloutManagement(targetManagement, deploymentManagement, rolloutGroupManagement,
distributionSetManagement, context, eventPublisher, virtualPropertyReplacer, txManager, tenantAware,
lockRegistry);
} }
/** /**
@@ -426,7 +433,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public RolloutGroupManagement rolloutGroupManagement() { RolloutGroupManagement rolloutGroupManagement() {
return new JpaRolloutGroupManagement(); return new JpaRolloutGroupManagement();
} }
@@ -437,7 +444,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public DeploymentManagement deploymentManagement() { DeploymentManagement deploymentManagement() {
return new JpaDeploymentManagement(); return new JpaDeploymentManagement();
} }
@@ -448,7 +455,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public ControllerManagement controllerManagement() { ControllerManagement controllerManagement() {
return new JpaControllerManagement(); return new JpaControllerManagement();
} }
@@ -460,7 +467,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public ArtifactManagement artifactManagement() { ArtifactManagement artifactManagement() {
return new JpaArtifactManagement(); return new JpaArtifactManagement();
} }
@@ -482,7 +489,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public EventEntityManagerHolder eventEntityManagerHolder() { EventEntityManagerHolder eventEntityManagerHolder() {
return EventEntityManagerHolder.getInstance(); return EventEntityManagerHolder.getInstance();
} }
@@ -497,7 +504,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public EventEntityManager eventEntityManager(final TenantAware aware, final EntityManager entityManager) { EventEntityManager eventEntityManager(final TenantAware aware, final EntityManager entityManager) {
return new JpaEventEntityManager(aware, entityManager); return new JpaEventEntityManager(aware, entityManager);
} }
@@ -516,7 +523,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public AutoAssignChecker autoAssignChecker(final TargetFilterQueryManagement targetFilterQueryManagement, AutoAssignChecker autoAssignChecker(final TargetFilterQueryManagement targetFilterQueryManagement,
final TargetManagement targetManagement, final DeploymentManagement deploymentManagement, final TargetManagement targetManagement, final DeploymentManagement deploymentManagement,
final PlatformTransactionManager transactionManager) { final PlatformTransactionManager transactionManager) {
return new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement, return new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement,
@@ -525,6 +532,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
/** /**
* {@link AutoAssignScheduler} bean. * {@link AutoAssignScheduler} bean.
*
* Note: does not activate in test profile, otherwise it is hard to test the
* auto assign functionality.
* *
* @param tenantAware * @param tenantAware
* to run as specific tenant * to run as specific tenant
@@ -534,14 +544,49 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* to run as system * to run as system
* @param autoAssignChecker * @param autoAssignChecker
* to run a check as tenant * to run a check as tenant
* @param lockRegistry
* to lock the tenant for auto assigment
* @return a new {@link AutoAssignChecker} * @return a new {@link AutoAssignChecker}
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
// don't active the auto assign scheduler in test, otherwise it is hard to
// test
@Profile("!test")
@ConditionalOnProperty(prefix = "hawkbit.autoassign.scheduler", name = "enabled", matchIfMissing = true) @ConditionalOnProperty(prefix = "hawkbit.autoassign.scheduler", name = "enabled", matchIfMissing = true)
public AutoAssignScheduler autoAssignScheduler(final TenantAware tenantAware, AutoAssignScheduler autoAssignScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext, final SystemSecurityContext systemSecurityContext, final AutoAssignChecker autoAssignChecker,
final AutoAssignChecker autoAssignChecker) { final LockRegistry lockRegistry) {
return new AutoAssignScheduler(tenantAware, systemManagement, systemSecurityContext, autoAssignChecker); return new AutoAssignScheduler(tenantAware, systemManagement, systemSecurityContext, autoAssignChecker,
lockRegistry);
}
/**
* {@link RolloutScheduler} bean.
*
* Note: does not activate in test profile, otherwise it is hard to test the
* rollout handling functionality.
*
* @param tenantAware
* to run as specific tenant
* @param systemManagement
* to find all tenants
* @param rolloutManagement
* to run the rollout handler
* @param systemSecurityContext
* to run as system
* @param threadPoolExecutor
* to execute the handlers in parallel
* @return a new {@link RolloutScheduler} bean.
*/
@Bean
@ConditionalOnMissingBean
@Profile("!test")
@ConditionalOnProperty(prefix = "hawkbit.rollout.scheduler", name = "enabled", matchIfMissing = true)
RolloutScheduler rolloutScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
final RolloutManagement rolloutManagement, final SystemSecurityContext systemSecurityContext,
@Qualifier("asyncExecutor") final Executor threadPoolExecutor) {
return new RolloutScheduler(tenantAware, systemManagement, rolloutManagement, systemSecurityContext,
threadPoolExecutor);
} }
} }

View File

@@ -60,8 +60,8 @@ public interface RolloutGroupRepository
* rollout-management to find out rolloutgroups which are in specific * rollout-management to find out rolloutgroups which are in specific
* states. * states.
* *
* @param rollout * @param rolloutId
* the rollout the rolloutgroup belong to * the ID of the rollout the rolloutgroup belong to
* @param rolloutGroupStatus1 * @param rolloutGroupStatus1
* the status of the rollout groups * the status of the rollout groups
* @param rolloutGroupStatus2 * @param rolloutGroupStatus2
@@ -69,22 +69,45 @@ public interface RolloutGroupRepository
* @return the count of rollout groups belonging to a rollout in specific * @return the count of rollout groups belonging to a rollout in specific
* status * status
*/ */
@Query("SELECT COUNT(r.id) FROM JpaRolloutGroup r WHERE r.rollout = :rollout and (r.status = :status1 or r.status = :status2)") @Query("SELECT COUNT(r.id) FROM JpaRolloutGroup r WHERE r.rollout.id = :rolloutId and (r.status = :status1 or r.status = :status2)")
Long countByRolloutAndStatusOrStatus(@Param("rollout") JpaRollout rollout, Long countByRolloutIdAndStatusOrStatus(@Param("rolloutId") long rolloutId,
@Param("status1") RolloutGroupStatus rolloutGroupStatus1, @Param("status1") RolloutGroupStatus rolloutGroupStatus1,
@Param("status2") RolloutGroupStatus rolloutGroupStatus2); @Param("status2") RolloutGroupStatus rolloutGroupStatus2);
/**
*
* Counts all rollout-groups refering to a given {@link Rollout} by its ID
* and groups which not having the given status.
*
* @param rolloutId
* the ID of the rollout refering the groups
* @param status1
* the status which the groups should not have
* @param status2
* the status which the groups should not have
* @param status2
* the status which the groups should not have
* @return count of rollout-groups referning a rollout and not in the given
* states
*/
long countByRolloutIdAndStatusNotAndStatusNotAndStatusNot(@Param("rolloutId") long rolloutId,
@Param("status1") JpaRolloutGroup.RolloutGroupStatus status1,
@Param("status2") JpaRolloutGroup.RolloutGroupStatus status2,
@Param("status3") JpaRolloutGroup.RolloutGroupStatus status3);
/** /**
* Retrieves all {@link RolloutGroup} for a specific parent in a specific * Retrieves all {@link RolloutGroup} for a specific parent in a specific
* status. Retrieves the child rolloutgroup for a specific status. * status. Retrieves the child rolloutgroup for a specific status.
* *
* @param rolloutGroup * @param rolloutGroupId
* the parent rolloutgroup * the rolloutgroupId to find the parents
* @param status * @param status
* the status of the rolloutgroups * the status of the rolloutgroups
* @return The child {@link RolloutGroup}s in a specific status * @return The child {@link RolloutGroup}s in a specific status
*/ */
List<JpaRolloutGroup> findByParentAndStatus(JpaRolloutGroup rolloutGroup, RolloutGroupStatus status); @Query("SELECT g FROM JpaRolloutGroup g WHERE g.parent.id=:rolloutGroupId and g.status=:status")
List<JpaRolloutGroup> findByParentIdAndStatus(@Param("rolloutGroupId") long rolloutGroupId,
@Param("status") RolloutGroupStatus status);
/** /**
* Updates all {@link RolloutGroup#getStatus()} of children for given * Updates all {@link RolloutGroup#getStatus()} of children for given
@@ -124,4 +147,8 @@ public interface RolloutGroupRepository
*/ */
Page<JpaRolloutGroup> findByRolloutId(final Long rolloutId, Pageable page); Page<JpaRolloutGroup> findByRolloutId(final Long rolloutId, Pageable page);
@Modifying
@Query("DELETE FROM JpaRolloutGroup g where g.id in :rolloutGroupIds")
void deleteByIds(@Param("rolloutGroupIds") List<Long> rolloutGroups);
} }

View File

@@ -8,6 +8,7 @@
*/ */
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@@ -15,11 +16,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus; import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
@@ -30,36 +28,15 @@ public interface RolloutRepository
extends BaseEntityRepository<JpaRollout, Long>, JpaSpecificationExecutor<JpaRollout> { extends BaseEntityRepository<JpaRollout, Long>, JpaSpecificationExecutor<JpaRollout> {
/** /**
* Updates the {@code lastCheck} field of the {@link Rollout} for rollouts * Retrieves all {@link Rollout} for given status.
* in a specific status and only if the {@code lastCheck} is overdue.
* *
* @param lastCheck
* the time in milliseconds to set to the lastCheck column
* @param delay
* the delay between last checks
* @param status * @param status
* the status which the rollout should have to update the last * the status of the rollouts to find
* check field * @return the list of {@link Rollout} for specific status
* @return the count of the updated rows. Zero if no row has been updated
*/ */
@Modifying // Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED) @Query("SELECT sm.id FROM JpaRollout sm WHERE sm.status IN ?1")
@Query("UPDATE JpaRollout r SET r.lastCheck = :lastCheck WHERE r.lastCheck < (:lastCheck - :delay) AND r.status=:status") List<Long> findByStatusIn(Collection<RolloutStatus> status);
int updateLastCheck(@Param("lastCheck") final long lastCheck, @Param("delay") final long delay,
@Param("status") final RolloutStatus status);
/**
* Retrieves all {@link Rollout} for a specific {@code lastCheck} time and
* for a specific status.
*
* @param lastCheck
* the lastCheck time to find the specific rollout.
* @param status
* the status of the rollout to find
* @return the list of {@link Rollout} for specific lastCheck time and
* status
*/
List<JpaRollout> findByLastCheckAndStatus(long lastCheck, RolloutStatus status);
/** /**
* Retrieves all {@link Rollout} for a specific {@code name} * Retrieves all {@link Rollout} for a specific {@code name}

View File

@@ -47,6 +47,15 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
@EntityGraph(value = "Target.detail", type = EntityGraphType.LOAD) @EntityGraph(value = "Target.detail", type = EntityGraphType.LOAD)
Optional<Target> findByControllerId(String controllerID); Optional<Target> findByControllerId(String controllerID);
/**
* Checks if target with given id exists.
*
* @param controllerId to check
* @return <code>true</code> if target with given id exists
*/
@Query("SELECT CASE WHEN COUNT(t)>0 THEN 'true' ELSE 'false' END FROM JpaTarget t WHERE t.controllerId=:controllerId")
boolean existsByControllerId(@Param("controllerId") String controllerId);
/** /**
* Deletes the {@link Target}s with the given target IDs. * Deletes the {@link Target}s with the given target IDs.
* *

View File

@@ -97,6 +97,7 @@ public class AutoAssignChecker {
*/ */
@Transactional(propagation = Propagation.REQUIRES_NEW) @Transactional(propagation = Propagation.REQUIRES_NEW)
public void check() { public void check() {
LOGGER.debug("Auto assigned check call");
final PageRequest pageRequest = new PageRequest(0, PAGE_SIZE); final PageRequest pageRequest = new PageRequest(0, PAGE_SIZE);

View File

@@ -9,27 +9,24 @@
package org.eclipse.hawkbit.repository.jpa.autoassign; package org.eclipse.hawkbit.repository.jpa.autoassign;
import java.util.List; import java.util.List;
import java.util.concurrent.locks.Lock;
import org.eclipse.hawkbit.repository.AutoAssignProperties;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
/** /**
* Scheduler to check target filters for auto assignment of distribution sets * Scheduler to check target filters for auto assignment of distribution sets
*/ */
// don't active the auto assign scheduler in test, otherwise it is hard to test
@Profile("!test")
@EnableConfigurationProperties(AutoAssignProperties.class)
public class AutoAssignScheduler { public class AutoAssignScheduler {
private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignScheduler.class); private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignScheduler.class);
private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.autoassign.scheduler.fixedDelay:2000}";
private final TenantAware tenantAware; private final TenantAware tenantAware;
private final SystemManagement systemManagement; private final SystemManagement systemManagement;
@@ -38,6 +35,8 @@ public class AutoAssignScheduler {
private final AutoAssignChecker autoAssignChecker; private final AutoAssignChecker autoAssignChecker;
private final LockRegistry lockRegistry;
/** /**
* Instantiates a new AutoAssignScheduler * Instantiates a new AutoAssignScheduler
* *
@@ -49,13 +48,17 @@ public class AutoAssignScheduler {
* to run as system * to run as system
* @param autoAssignChecker * @param autoAssignChecker
* to run a check as tenant * to run a check as tenant
* @param lockRegistry
* to acquire a lock per tenant
*/ */
public AutoAssignScheduler(final TenantAware tenantAware, final SystemManagement systemManagement, public AutoAssignScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
final SystemSecurityContext systemSecurityContext, final AutoAssignChecker autoAssignChecker) { final SystemSecurityContext systemSecurityContext, final AutoAssignChecker autoAssignChecker,
final LockRegistry lockRegistry) {
this.tenantAware = tenantAware; this.tenantAware = tenantAware;
this.systemManagement = systemManagement; this.systemManagement = systemManagement;
this.systemSecurityContext = systemSecurityContext; this.systemSecurityContext = systemSecurityContext;
this.autoAssignChecker = autoAssignChecker; this.autoAssignChecker = autoAssignChecker;
this.lockRegistry = lockRegistry;
} }
/** /**
@@ -64,29 +67,38 @@ public class AutoAssignScheduler {
* tenant the auto assignments defined in the target filter queries * tenant the auto assignments defined in the target filter queries
* {@link SystemSecurityContext}. * {@link SystemSecurityContext}.
*/ */
@Scheduled(initialDelayString = AutoAssignProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = AutoAssignProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER) @Scheduled(initialDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER)
public void autoAssignScheduler() { public void autoAssignScheduler() {
LOGGER.debug("auto assign schedule checker has been triggered."); LOGGER.debug("auto assign schedule checker has been triggered.");
// run this code in system code privileged to have the necessary // run this code in system code privileged to have the necessary
// permission to query and create entities. // permission to query and create entities.
systemSecurityContext.runAsSystem(() -> { systemSecurityContext.runAsSystem(() -> executeAutoAssign());
// workaround eclipselink that is currently not possible to }
// execute a query without multitenancy if MultiTenant
// annotation is used. private Object executeAutoAssign() {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So // workaround eclipselink that is currently not possible to
// iterate through all tenants and execute the rollout check for // execute a query without multitenancy if MultiTenant
// each tenant separately. // annotation is used.
final List<String> tenants = systemManagement.findTenants(); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
LOGGER.info("Checking target filter queries for tenants: {}", tenants.size()); // iterate through all tenants and execute the rollout check for
for (final String tenant : tenants) { // each tenant separately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking target filter queries for tenants: {}", tenants.size());
for (final String tenant : tenants) {
final Lock lock = lockRegistry.obtain(tenant + "-autoassign");
if (!lock.tryLock()) {
return null;
}
try {
tenantAware.runAsTenant(tenant, () -> { tenantAware.runAsTenant(tenant, () -> {
autoAssignChecker.check(); autoAssignChecker.check();
return null; return null;
}); });
} finally {
lock.unlock();
} }
return null; }
}); return null;
} }
} }

View File

@@ -43,12 +43,12 @@ public class DistributionSetTypeElement implements Serializable {
@MapsId("dsType") @MapsId("dsType")
@ManyToOne(optional = false, fetch = FetchType.LAZY) @ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "distribution_set_type", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_type_element_dstype")) @JoinColumn(name = "distribution_set_type", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_type_element_element"))
private JpaDistributionSetType dsType; private JpaDistributionSetType dsType;
@MapsId("smType") @MapsId("smType")
@ManyToOne(optional = false, fetch = FetchType.LAZY) @ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "software_module_type", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_type_element_smtype")) @JoinColumn(name = "software_module_type", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_type_element_smtype"))
private JpaSoftwareModuleType smType; private JpaSoftwareModuleType smType;
public DistributionSetTypeElement() { public DistributionSetTypeElement() {

View File

@@ -20,10 +20,10 @@ import javax.persistence.Embeddable;
public class DistributionSetTypeElementCompositeKey implements Serializable { public class DistributionSetTypeElementCompositeKey implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Column(name = "distribution_set_type", nullable = false) @Column(name = "distribution_set_type", nullable = false, updatable = false)
private Long dsType; private Long dsType;
@Column(name = "software_module_type", nullable = false) @Column(name = "software_module_type", nullable = false, updatable = false)
private Long smType; private Long smType;
/** /**

View File

@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa.model;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.ConstraintMode; import javax.persistence.ConstraintMode;
import javax.persistence.Entity; import javax.persistence.Entity;
@@ -34,6 +33,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
@@ -58,13 +58,13 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Action, EventAwareEntity { public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Action, EventAwareEntity {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "distribution_set", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_ds")) @JoinColumn(name = "distribution_set", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_ds"))
@NotNull @NotNull
private JpaDistributionSet distributionSet; private JpaDistributionSet distributionSet;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "target", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_target")) @JoinColumn(name = "target", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_target"))
@NotNull @NotNull
private JpaTarget target; private JpaTarget target;
@@ -79,20 +79,20 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Column(name = "forced_time") @Column(name = "forced_time")
private long forcedTime; private long forcedTime;
@Column(name = "status") @Column(name = "status", nullable = false)
@NotNull
private Status status; private Status status;
@CascadeOnDelete @CascadeOnDelete
@OneToMany(mappedBy = "action", targetEntity = JpaActionStatus.class, fetch = FetchType.LAZY, cascade = { @OneToMany(mappedBy = "action", targetEntity = JpaActionStatus.class, fetch = FetchType.LAZY)
CascadeType.REMOVE }) private List<JpaActionStatus> actionStatus;
private List<ActionStatus> actionStatus;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "rolloutgroup", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rolloutgroup")) @JoinColumn(name = "rolloutgroup", updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rolloutgroup"))
private JpaRolloutGroup rolloutGroup; private JpaRolloutGroup rolloutGroup;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "rollout", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rollout")) @JoinColumn(name = "rollout", updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rollout"))
private JpaRollout rollout; private JpaRollout rollout;
@Override @Override
@@ -185,13 +185,15 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Override @Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) { public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher() EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new ActionCreatedEvent(this, EventPublisherHolder.getInstance().getApplicationId())); .publishEvent(new ActionCreatedEvent(this, BaseEntity.getIdOrNull(rollout),
BaseEntity.getIdOrNull(rolloutGroup), EventPublisherHolder.getInstance().getApplicationId()));
} }
@Override @Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) { public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher() EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new ActionUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId())); .publishEvent(new ActionUpdatedEvent(this, BaseEntity.getIdOrNull(rollout),
BaseEntity.getIdOrNull(rolloutGroup), EventPublisherHolder.getInstance().getApplicationId()));
} }
@Override @Override

View File

@@ -54,11 +54,11 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
private Long occurredAt; private Long occurredAt;
@ManyToOne(fetch = FetchType.LAZY, optional = false) @ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "action", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_act_stat_action")) @JoinColumn(name = "action", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_act_stat_action"))
@NotNull @NotNull
private JpaAction action; private JpaAction action;
@Column(name = "status") @Column(name = "status", nullable = false, updatable = false)
@NotNull @NotNull
private Status status; private Status status;

View File

@@ -12,6 +12,7 @@ import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.ConstraintMode; import javax.persistence.ConstraintMode;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey; import javax.persistence.ForeignKey;
import javax.persistence.Index; import javax.persistence.Index;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
@@ -49,7 +50,7 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art
@NotEmpty @NotEmpty
private String filename; private String filename;
@ManyToOne(optional = false, cascade = { CascadeType.PERSIST }) @ManyToOne(optional = false, cascade = { CascadeType.PERSIST }, fetch = FetchType.LAZY)
@JoinColumn(name = "software_module", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_assigned_sm")) @JoinColumn(name = "software_module", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_assigned_sm"))
private JpaSoftwareModule softwareModule; private JpaSoftwareModule softwareModule;

View File

@@ -16,7 +16,6 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.ConstraintMode; import javax.persistence.ConstraintMode;
import javax.persistence.Entity; import javax.persistence.Entity;
@@ -45,9 +44,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete; import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent; import org.eclipse.persistence.descriptors.DescriptorEvent;
@@ -82,40 +79,38 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
@CascadeOnDelete @CascadeOnDelete
@ManyToMany(targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY) @ManyToMany(targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
@JoinTable(name = "sp_ds_module", joinColumns = { @JoinTable(name = "sp_ds_module", joinColumns = {
@JoinColumn(name = "ds_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_ds")) }, inverseJoinColumns = { @JoinColumn(name = "ds_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_ds")) }, inverseJoinColumns = {
@JoinColumn(name = "module_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_module")) }) @JoinColumn(name = "module_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_module")) })
private Set<SoftwareModule> modules; private Set<SoftwareModule> modules;
@CascadeOnDelete @CascadeOnDelete
@ManyToMany(targetEntity = JpaDistributionSetTag.class) @ManyToMany(targetEntity = JpaDistributionSetTag.class)
@JoinTable(name = "sp_ds_dstag", joinColumns = { @JoinTable(name = "sp_ds_dstag", joinColumns = {
@JoinColumn(name = "ds", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_ds")) }, inverseJoinColumns = { @JoinColumn(name = "ds", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_ds")) }, inverseJoinColumns = {
@JoinColumn(name = "TAG", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_tag")) }) @JoinColumn(name = "TAG", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_tag")) })
private Set<DistributionSetTag> tags; private Set<DistributionSetTag> tags;
@Column(name = "deleted") @Column(name = "deleted")
private boolean deleted; private boolean deleted;
@OneToMany(mappedBy = "assignedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY) @OneToMany(mappedBy = "assignedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
private List<Target> assignedToTargets; private List<JpaTarget> assignedToTargets;
@OneToMany(mappedBy = "autoAssignDistributionSet", targetEntity = JpaTargetFilterQuery.class, fetch = FetchType.LAZY) @OneToMany(mappedBy = "autoAssignDistributionSet", targetEntity = JpaTargetFilterQuery.class, fetch = FetchType.LAZY)
private List<TargetFilterQuery> autoAssignFilters; private List<TargetFilterQuery> autoAssignFilters;
@OneToMany(mappedBy = "installedDistributionSet", targetEntity = JpaTargetInfo.class, fetch = FetchType.LAZY) @OneToMany(mappedBy = "installedDistributionSet", targetEntity = JpaTargetInfo.class, fetch = FetchType.LAZY)
private List<TargetInfo> installedAtTargets; private List<JpaTargetInfo> installedAtTargets;
@OneToMany(mappedBy = "distributionSet", targetEntity = JpaAction.class, fetch = FetchType.LAZY) @OneToMany(mappedBy = "distributionSet", targetEntity = JpaAction.class, fetch = FetchType.LAZY)
private List<Action> actions; private List<JpaAction> actions;
@CascadeOnDelete @CascadeOnDelete
@OneToMany(fetch = FetchType.LAZY, targetEntity = JpaDistributionSetMetadata.class, cascade = { @OneToMany(mappedBy = "distributionSet", fetch = FetchType.LAZY, targetEntity = JpaDistributionSetMetadata.class)
CascadeType.REMOVE })
@JoinColumn(name = "ds_id", insertable = false, updatable = false)
private List<DistributionSetMetadata> metadata; private List<DistributionSetMetadata> metadata;
@ManyToOne(fetch = FetchType.LAZY, targetEntity = JpaDistributionSetType.class) @ManyToOne(fetch = FetchType.LAZY, optional = false, targetEntity = JpaDistributionSetType.class)
@JoinColumn(name = "ds_id", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstype_ds")) @JoinColumn(name = "ds_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstype_ds"))
@NotNull @NotNull
private DistributionSetType type; private DistributionSetType type;

View File

@@ -32,8 +32,8 @@ public class JpaDistributionSetMetadata extends JpaMetaData implements Distribut
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Id @Id
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "ds_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_ds")) @JoinColumn(name = "ds_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_ds"))
private JpaDistributionSet distributionSet; private JpaDistributionSet distributionSet;
public JpaDistributionSetMetadata() { public JpaDistributionSetMetadata() {

View File

@@ -19,7 +19,6 @@ import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;
import javax.persistence.Index; import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.UniqueConstraint; import javax.persistence.UniqueConstraint;
@@ -29,6 +28,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@@ -49,12 +49,12 @@ import org.springframework.util.CollectionUtils;
public class JpaDistributionSetType extends AbstractJpaNamedEntity implements DistributionSetType { public class JpaDistributionSetType extends AbstractJpaNamedEntity implements DistributionSetType {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@OneToMany(targetEntity = DistributionSetTypeElement.class, cascade = { @CascadeOnDelete
CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true) @OneToMany(mappedBy = "dsType", targetEntity = DistributionSetTypeElement.class, cascade = {
@JoinColumn(name = "distribution_set_type", insertable = false, updatable = false) CascadeType.PERSIST }, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<DistributionSetTypeElement> elements; private Set<DistributionSetTypeElement> elements;
@Column(name = "type_key", nullable = false, length = 64) @Column(name = "type_key", nullable = false, updatable = false, length = 64)
@Size(max = 64) @Size(max = 64)
@NotEmpty @NotEmpty
private String key; private String key;

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.ConstraintMode; import javax.persistence.ConstraintMode;
@@ -28,6 +29,7 @@ import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -35,7 +37,14 @@ import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus; import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.annotations.ConversionValue;
import org.eclipse.persistence.annotations.Convert;
import org.eclipse.persistence.annotations.ObjectTypeConverter;
import org.eclipse.persistence.descriptors.DescriptorEvent; import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.queries.UpdateObjectQuery;
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
/** /**
@@ -49,25 +58,41 @@ import org.hibernate.validator.constraints.NotEmpty;
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for // exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
// sub entities // sub entities
@SuppressWarnings("squid:S2160") @SuppressWarnings("squid:S2160")
@ObjectTypeConverter(name = "rolloutstatus", objectType = Rollout.RolloutStatus.class, dataType = Integer.class, conversionValues = {
@ConversionValue(objectValue = "CREATING", dataValue = "0"),
@ConversionValue(objectValue = "READY", dataValue = "1"),
@ConversionValue(objectValue = "PAUSED", dataValue = "2"),
@ConversionValue(objectValue = "STARTING", dataValue = "3"),
@ConversionValue(objectValue = "STOPPED", dataValue = "4"),
@ConversionValue(objectValue = "RUNNING", dataValue = "5"),
@ConversionValue(objectValue = "FINISHED", dataValue = "6"),
@ConversionValue(objectValue = "ERROR_CREATING", dataValue = "7"),
@ConversionValue(objectValue = "ERROR_STARTING", dataValue = "8"),
@ConversionValue(objectValue = "DELETING", dataValue = "9"),
@ConversionValue(objectValue = "DELETED", dataValue = "10") })
public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, EventAwareEntity { public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, EventAwareEntity {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@OneToMany(targetEntity = JpaRolloutGroup.class) private static final String DELETED_PROPERTY = "deleted";
@JoinColumn(name = "rollout", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollout_rolloutgroup"))
private List<RolloutGroup> rolloutGroups; @CascadeOnDelete
@OneToMany(targetEntity = JpaRolloutGroup.class, fetch = FetchType.LAZY, mappedBy = "rollout")
private List<JpaRolloutGroup> rolloutGroups;
@Column(name = "target_filter", length = 1024, nullable = false) @Column(name = "target_filter", length = 1024, nullable = false)
@Size(max = 1024) @Size(max = 1024)
@NotEmpty @NotEmpty
private String targetFilterQuery; private String targetFilterQuery;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "distribution_set", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolltout_ds")) @JoinColumn(name = "distribution_set", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolltout_ds"))
@NotNull @NotNull
private JpaDistributionSet distributionSet; private JpaDistributionSet distributionSet;
@Column(name = "status") @Column(name = "status", nullable = false)
@Convert("rolloutstatus")
@NotNull
private RolloutStatus status = RolloutStatus.CREATING; private RolloutStatus status = RolloutStatus.CREATING;
@Column(name = "last_check") @Column(name = "last_check")
@@ -87,6 +112,9 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
@Column(name = "rollout_groups_created") @Column(name = "rollout_groups_created")
private int rolloutGroupsCreated; private int rolloutGroupsCreated;
@Column(name = "deleted")
private boolean deleted;
@Column(name = "start_at") @Column(name = "start_at")
private Long startAt; private Long startAt;
@@ -142,7 +170,7 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
return startAt; return startAt;
} }
public void setStartAt(Long startAt) { public void setStartAt(final Long startAt) {
this.startAt = startAt; this.startAt = startAt;
} }
@@ -196,9 +224,8 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
@Override @Override
public String toString() { public String toString() {
return "Rollout [rolloutGroups=" + rolloutGroups + ", targetFilterQuery=" + targetFilterQuery return "Rollout [ targetFilterQuery=" + targetFilterQuery + ", distributionSet=" + distributionSet + ", status="
+ ", distributionSet=" + distributionSet + ", status=" + status + ", lastCheck=" + lastCheck + status + ", lastCheck=" + lastCheck + ", getName()=" + getName() + ", getId()=" + getId() + "]";
+ ", getName()=" + getName() + ", getId()=" + getId() + "]";
} }
@Override @Override
@@ -211,11 +238,35 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
EventPublisherHolder.getInstance().getEventPublisher() EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new RolloutUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId())); .publishEvent(new RolloutUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
if (isSoftDeleted(descriptorEvent)) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutDeletedEvent(getTenant(),
getId(), getClass().getName(), EventPublisherHolder.getInstance().getApplicationId()));
}
}
private static boolean isSoftDeleted(final DescriptorEvent event) {
final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
final List<DirectToFieldChangeRecord> changes = changeSet.getChanges().stream()
.filter(record -> record instanceof DirectToFieldChangeRecord)
.map(record -> (DirectToFieldChangeRecord) record).collect(Collectors.toList());
return changes.stream().filter(record -> DELETED_PROPERTY.equals(record.getAttribute())
&& Boolean.parseBoolean(record.getNewValue().toString())).count() > 0;
} }
@Override @Override
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) { public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
// there is no rollout deletion event EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutDeletedEvent(getTenant(),
getId(), getClass().getName(), EventPublisherHolder.getInstance().getApplicationId()));
}
@Override
public boolean isDeleted() {
return deleted;
}
public void setDeleted(final boolean deleted) {
this.deleted = deleted;
} }
} }

View File

@@ -32,6 +32,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus; import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.annotations.ConversionValue; import org.eclipse.persistence.annotations.ConversionValue;
import org.eclipse.persistence.annotations.Convert; import org.eclipse.persistence.annotations.Convert;
import org.eclipse.persistence.annotations.ObjectTypeConverter; import org.eclipse.persistence.annotations.ObjectTypeConverter;
@@ -60,18 +61,20 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "rollout", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolloutgroup_rollout")) @JoinColumn(name = "rollout", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolloutgroup_rollout"))
private JpaRollout rollout; private JpaRollout rollout;
@Column(name = "status") @Column(name = "status", nullable = false)
@Convert("rolloutgroupstatus") @Convert("rolloutgroupstatus")
private RolloutGroupStatus status = RolloutGroupStatus.CREATING; private RolloutGroupStatus status = RolloutGroupStatus.CREATING;
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST }, targetEntity = RolloutTargetGroup.class) @CascadeOnDelete
@JoinColumn(name = "rolloutGroup_Id", insertable = false, updatable = false) @OneToMany(mappedBy = "rolloutGroup", fetch = FetchType.LAZY, cascade = {
CascadeType.PERSIST }, targetEntity = RolloutTargetGroup.class)
private List<RolloutTargetGroup> rolloutTargetGroup; private List<RolloutTargetGroup> rolloutTargetGroup;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolloutgroup_rolloutgroup"))
private JpaRolloutGroup parent; private JpaRolloutGroup parent;
@Column(name = "success_condition", nullable = false) @Column(name = "success_condition", nullable = false)
@@ -287,8 +290,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
@Override @Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) { public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutGroupUpdatedEvent(this, EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutGroupUpdatedEvent(this,
this.getRollout().getId(), EventPublisherHolder.getInstance().getApplicationId()));
EventPublisherHolder.getInstance().getApplicationId()));
} }
@Override @Override

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;
@@ -36,11 +37,13 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedE
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete; import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent; import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.queries.UpdateObjectQuery;
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
/** /**
* Base Software Module that is supported by OS level provisioning mechanism on * Base Software Module that is supported by OS level provisioning mechanism on
@@ -60,8 +63,10 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implements SoftwareModule, EventAwareEntity { public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implements SoftwareModule, EventAwareEntity {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String DELETED_PROPERTY = "deleted";
@ManyToOne @ManyToOne
@JoinColumn(name = "module_type", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_module_type")) @JoinColumn(name = "module_type", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_module_type"))
@NotNull @NotNull
private JpaSoftwareModuleType type; private JpaSoftwareModuleType type;
@@ -77,13 +82,12 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
@CascadeOnDelete @CascadeOnDelete
@OneToMany(fetch = FetchType.LAZY, mappedBy = "softwareModule", cascade = { @OneToMany(fetch = FetchType.LAZY, mappedBy = "softwareModule", cascade = {
CascadeType.ALL }, targetEntity = JpaArtifact.class) CascadeType.PERSIST }, targetEntity = JpaArtifact.class, orphanRemoval = true)
private List<Artifact> artifacts; private List<JpaArtifact> artifacts;
@CascadeOnDelete @CascadeOnDelete
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaSoftwareModuleMetadata.class) @OneToMany(mappedBy = "softwareModule", fetch = FetchType.LAZY, targetEntity = JpaSoftwareModuleMetadata.class)
@JoinColumn(name = "sw_id", insertable = false, updatable = false) private List<JpaSoftwareModuleMetadata> metadata;
private List<SoftwareModuleMetadata> metadata;
/** /**
* Default constructor. * Default constructor.
@@ -116,12 +120,12 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
public void addArtifact(final Artifact artifact) { public void addArtifact(final Artifact artifact) {
if (null == artifacts) { if (null == artifacts) {
artifacts = new ArrayList<>(4); artifacts = new ArrayList<>(4);
artifacts.add(artifact); artifacts.add((JpaArtifact) artifact);
return; return;
} }
if (!artifacts.contains(artifact)) { if (!artifacts.contains(artifact)) {
artifacts.add(artifact); artifacts.add((JpaArtifact) artifact);
} }
} }
@@ -206,6 +210,21 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) { public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent( EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
new SoftwareModuleUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId())); new SoftwareModuleUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
if (isSoftDeleted(descriptorEvent)) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new SoftwareModuleDeletedEvent(
getTenant(), getId(), getClass().getName(), EventPublisherHolder.getInstance().getApplicationId()));
}
}
private static boolean isSoftDeleted(final DescriptorEvent event) {
final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
final List<DirectToFieldChangeRecord> changes = changeSet.getChanges().stream()
.filter(record -> record instanceof DirectToFieldChangeRecord)
.map(record -> (DirectToFieldChangeRecord) record).collect(Collectors.toList());
return changes.stream().filter(record -> DELETED_PROPERTY.equals(record.getAttribute())
&& Boolean.parseBoolean(record.getNewValue().toString())).count() > 0;
} }
@Override @Override

View File

@@ -32,8 +32,8 @@ public class JpaSoftwareModuleMetadata extends JpaMetaData implements SoftwareMo
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Id @Id
@ManyToOne(targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY) @ManyToOne(optional = false, targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
@JoinColumn(name = "sw_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_sw")) @JoinColumn(name = "sw_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_sw"))
private SoftwareModule softwareModule; private SoftwareModule softwareModule;
public JpaSoftwareModuleMetadata() { public JpaSoftwareModuleMetadata() {

View File

@@ -87,22 +87,21 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
@CascadeOnDelete @CascadeOnDelete
@ManyToMany(targetEntity = JpaTargetTag.class) @ManyToMany(targetEntity = JpaTargetTag.class)
@JoinTable(name = "sp_target_target_tag", joinColumns = { @JoinTable(name = "sp_target_target_tag", joinColumns = {
@JoinColumn(name = "target", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_target")) }, inverseJoinColumns = { @JoinColumn(name = "target", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_target")) }, inverseJoinColumns = {
@JoinColumn(name = "tag", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_tag")) }) @JoinColumn(name = "tag", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_tag")) })
private Set<TargetTag> tags; private Set<TargetTag> tags;
@CascadeOnDelete @CascadeOnDelete
@OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, cascade = { @OneToMany(mappedBy = "target", fetch = FetchType.LAZY, targetEntity = JpaAction.class)
CascadeType.REMOVE }, targetEntity = JpaAction.class) private List<JpaAction> actions;
@JoinColumn(name = "target", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_act_hist_targ"))
private List<Action> actions;
@ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = JpaDistributionSet.class) @ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = JpaDistributionSet.class)
@JoinColumn(name = "assigned_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds")) @JoinColumn(name = "assigned_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds"))
private JpaDistributionSet assignedDistributionSet; private JpaDistributionSet assignedDistributionSet;
@CascadeOnDelete @CascadeOnDelete
@OneToOne(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY, targetEntity = JpaTargetInfo.class) @OneToOne(cascade = { CascadeType.PERSIST,
CascadeType.MERGE }, fetch = FetchType.LAZY, targetEntity = JpaTargetInfo.class)
@PrimaryKeyJoinColumn @PrimaryKeyJoinColumn
private JpaTargetInfo targetInfo; private JpaTargetInfo targetInfo;
@@ -110,14 +109,13 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
* the security token of the target which allows if enabled to authenticate * the security token of the target which allows if enabled to authenticate
* with this security token. * with this security token.
*/ */
@Column(name = "sec_token", insertable = true, updatable = true, nullable = false, length = 128) @Column(name = "sec_token", updatable = true, nullable = false, length = 128)
@Size(max = 64) @Size(max = 128)
@NotEmpty @NotEmpty
private String securityToken; private String securityToken;
@CascadeOnDelete @CascadeOnDelete
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE, CascadeType.PERSIST }) @OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
@JoinColumn(name = "target_Id", insertable = false, updatable = false)
private List<RolloutTargetGroup> rolloutTargetGroup; private List<RolloutTargetGroup> rolloutTargetGroup;
/** /**
@@ -214,7 +212,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
actions = new ArrayList<>(); actions = new ArrayList<>();
} }
return actions.add(action); return actions.add((JpaAction) action);
} }
@Override @Override

View File

@@ -79,8 +79,7 @@ public class JpaTargetInfo implements Persistable<Long>, TargetInfo {
private boolean entityNew; private boolean entityNew;
@CascadeOnDelete @CascadeOnDelete
@OneToOne(cascade = { CascadeType.MERGE, @OneToOne(cascade = { CascadeType.MERGE }, fetch = FetchType.LAZY, targetEntity = JpaTarget.class)
CascadeType.REMOVE }, fetch = FetchType.LAZY, targetEntity = JpaTarget.class)
@JoinColumn(name = "target_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_stat_targ")) @JoinColumn(name = "target_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_stat_targ"))
@MapsId @MapsId
private JpaTarget target; private JpaTarget target;
@@ -112,7 +111,7 @@ public class JpaTargetInfo implements Persistable<Long>, TargetInfo {
@Column(name = "attribute_value", length = 128) @Column(name = "attribute_value", length = 128)
@MapKeyColumn(name = "attribute_key", nullable = false, length = 32) @MapKeyColumn(name = "attribute_key", nullable = false, length = 32)
@CollectionTable(name = "sp_target_attributes", joinColumns = { @CollectionTable(name = "sp_target_attributes", joinColumns = {
@JoinColumn(name = "target_id") }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target")) @JoinColumn(name = "target_id", nullable = false, updatable = false) }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target"))
private final Map<String, String> controllerAttributes = Collections.synchronizedMap(new HashMap<String, String>()); private final Map<String, String> controllerAttributes = Collections.synchronizedMap(new HashMap<String, String>());
// set default request controller attributes to true, because we want to // set default request controller attributes to true, because we want to

View File

@@ -32,7 +32,7 @@ import org.hibernate.validator.constraints.NotEmpty;
public class JpaTenantConfiguration extends AbstractJpaTenantAwareBaseEntity implements TenantConfiguration { public class JpaTenantConfiguration extends AbstractJpaTenantAwareBaseEntity implements TenantConfiguration {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Column(name = "conf_key", length = 128, nullable = false) @Column(name = "conf_key", length = 128, nullable = false, updatable = false)
@Size(max = 128) @Size(max = 128)
@NotEmpty @NotEmpty
private String key; private String key;

View File

@@ -43,7 +43,7 @@ import org.eclipse.hawkbit.repository.model.TenantMetaData;
public class JpaTenantMetaData extends AbstractJpaBaseEntity implements TenantMetaData { public class JpaTenantMetaData extends AbstractJpaBaseEntity implements TenantMetaData {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Column(name = "tenant", nullable = false, length = 40) @Column(name = "tenant", nullable = false, updatable = false, length = 40)
@Size(max = 40) @Size(max = 40)
private String tenant; private String tenant;

View File

@@ -24,7 +24,6 @@ import javax.persistence.ManyToOne;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import javax.persistence.Table; import javax.persistence.Table;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.persistence.annotations.ExistenceChecking; import org.eclipse.persistence.annotations.ExistenceChecking;
@@ -44,19 +43,22 @@ public class RolloutTargetGroup implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Id @Id
@ManyToOne(targetEntity = JpaRolloutGroup.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST }) @ManyToOne(optional = false, targetEntity = JpaRolloutGroup.class, fetch = FetchType.LAZY, cascade = {
@JoinColumn(name = "rolloutGroup_Id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_group")) CascadeType.PERSIST })
private RolloutGroup rolloutGroup; @JoinColumn(name = "rolloutGroup_Id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_group"))
private JpaRolloutGroup rolloutGroup;
@Id @Id
@ManyToOne(targetEntity = JpaTarget.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST }) @ManyToOne(optional = false, targetEntity = JpaTarget.class, fetch = FetchType.LAZY, cascade = {
@JoinColumn(name = "target_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_target")) CascadeType.PERSIST })
@JoinColumn(name = "target_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_target"))
private JpaTarget target; private JpaTarget target;
@OneToMany(targetEntity = JpaAction.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST }) @OneToMany(targetEntity = JpaAction.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
@JoinColumns(value = { @JoinColumn(name = "rolloutgroup", referencedColumnName = "rolloutGroup_Id"), @JoinColumns(value = {
@JoinColumn(name = "target", referencedColumnName = "target_id") }) @JoinColumn(name = "rolloutgroup", nullable = false, updatable = false, referencedColumnName = "rolloutGroup_Id"),
private List<Action> actions; @JoinColumn(name = "target", nullable = false, updatable = false, referencedColumnName = "target_id") })
private List<JpaAction> actions;
/** /**
* default constructor for JPA. * default constructor for JPA.
@@ -66,7 +68,7 @@ public class RolloutTargetGroup implements Serializable {
} }
public RolloutTargetGroup(final RolloutGroup rolloutGroup, final Target target) { public RolloutTargetGroup(final RolloutGroup rolloutGroup, final Target target) {
this.rolloutGroup = rolloutGroup; this.rolloutGroup = (JpaRolloutGroup) rolloutGroup;
this.target = (JpaTarget) target; this.target = (JpaTarget) target;
} }

View File

@@ -9,63 +9,78 @@
package org.eclipse.hawkbit.repository.jpa.rollout; package org.eclipse.hawkbit.repository.jpa.rollout;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorCompletionService;
import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutProperties;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.google.common.base.Throwables;
/** /**
* Scheduler to schedule the * Scheduler to schedule the {@link RolloutManagement#handleRollouts()}. The
* {@link RolloutManagement#checkRunningRollouts(long)}. The delay between the * delay between the checks be be configured using the property from
* checks be be configured using the properties from {@link RolloutProperties}. * {#PROP_SCHEDULER_DELAY_PLACEHOLDER}.
*/ */
@Component
// don't active the rollout scheduler in test, otherwise it is hard to test
// rolloutmanagement and leads weird side-effects maybe.
@Profile("!test")
public class RolloutScheduler { public class RolloutScheduler {
private static final Logger LOGGER = LoggerFactory.getLogger(RolloutScheduler.class); private static final Logger LOGGER = LoggerFactory.getLogger(RolloutScheduler.class);
@Autowired private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.scheduler.fixedDelay:2000}";
private TenantAware tenantAware;
@Autowired private final TenantAware tenantAware;
private SystemManagement systemManagement;
@Autowired private final SystemManagement systemManagement;
private RolloutManagement rolloutManagement;
@Autowired private final RolloutManagement rolloutManagement;
private SystemSecurityContext systemSecurityContext;
@Autowired private final SystemSecurityContext systemSecurityContext;
private RolloutProperties rolloutProperties;
private final ExecutorCompletionService<Void> completionService;
/**
* Constructor.
*
* @param tenantAware
* to run as specific tenant
* @param systemManagement
* to find all tenants
* @param rolloutManagement
* to run the rollout handler
* @param systemSecurityContext
* to run as system
* @param threadPoolExecutor
* to execute the handlers in parallel
*/
public RolloutScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
final RolloutManagement rolloutManagement, final SystemSecurityContext systemSecurityContext,
final Executor threadPoolExecutor) {
this.tenantAware = tenantAware;
this.systemManagement = systemManagement;
this.rolloutManagement = rolloutManagement;
this.systemSecurityContext = systemSecurityContext;
completionService = new ExecutorCompletionService<>(threadPoolExecutor);
}
/** /**
* Scheduler method called by the spring-async mechanism. Retrieves all * Scheduler method called by the spring-async mechanism. Retrieves all
* tenants from the {@link SystemManagement#findTenants()} and runs for each * tenants from the {@link SystemManagement#findTenants()} and runs for each
* tenant the {@link RolloutManagement#checkRunningRollouts(long)} in the * tenant the {@link RolloutManagement#handleRollouts()} in the
* {@link SystemSecurityContext}. * {@link SystemSecurityContext}.
*/ */
@Scheduled(initialDelayString = RolloutProperties.PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = RolloutProperties.PROP_SCHEDULER_DELAY_PLACEHOLDER) @Scheduled(initialDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER)
public void runningRolloutScheduler() { public void runningRolloutScheduler() {
if (!rolloutProperties.getScheduler().isEnabled()) {
return;
}
LOGGER.debug("rollout schedule checker has been triggered."); LOGGER.debug("rollout schedule checker has been triggered.");
// run this code in system code privileged to have the necessary // run this code in system code privileged to have the necessary
// permission to query and create entities. // permission to query and create entities.
systemSecurityContext.runAsSystem(() -> { final int tasks = systemSecurityContext.runAsSystem(() -> {
// workaround eclipselink that is currently not possible to // workaround eclipselink that is currently not possible to
// execute a query without multitenancy if MultiTenant // execute a query without multitenancy if MultiTenant
// annotation is used. // annotation is used.
@@ -75,119 +90,25 @@ public class RolloutScheduler {
final List<String> tenants = systemManagement.findTenants(); final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking rollouts for {} tenants", tenants.size()); LOGGER.info("Checking rollouts for {} tenants", tenants.size());
for (final String tenant : tenants) { for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> { completionService.submit(() -> tenantAware.runAsTenant(tenant, () -> {
final long fixedDelay = rolloutProperties.getScheduler().getFixedDelay(); rolloutManagement.handleRollouts();
rolloutManagement.checkRunningRollouts(fixedDelay);
return null; return null;
}); }));
} }
return null; return tenants.size();
}); });
waitUntilHandlersAreComplete(tasks);
} }
/** private void waitUntilHandlersAreComplete(final int tasks) {
* Scheduler method called by the spring-async mechanism. Retrieves all try {
* tenants from the {@link SystemManagement#findTenants()} and runs for each for (int i = 0; i < tasks; i++) {
* tenant the {@link RolloutManagement#checkStartingRollouts(long)} in the completionService.take().get();
* {@link SystemSecurityContext}.
*/
@Scheduled(initialDelayString = RolloutProperties.PROP_STARTING_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = RolloutProperties.PROP_STARTING_SCHEDULER_DELAY_PLACEHOLDER)
public void startingRolloutScheduler() {
if (!rolloutProperties.getStartingScheduler().isEnabled()) {
return;
}
LOGGER.debug("rollout starting schedule checker has been triggered.");
// run this code in system code privileged to have the necessary
// permission to query and create entities.
systemSecurityContext.runAsSystem(() -> {
// workaround eclipselink that is currently not possible to
// execute a query without multitenancy if MultiTenant
// annotation is used.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for
// each tenant seperately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking starting rollouts for {} tenants", tenants.size());
for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> {
final long fixedDelay = rolloutProperties.getStartingScheduler().getFixedDelay();
rolloutManagement.checkStartingRollouts(fixedDelay);
return null;
});
} }
return null; } catch (InterruptedException | ExecutionException e) {
}); Thread.currentThread().interrupt();
} throw Throwables.propagate(e);
/**
* Scheduler method called by the spring-async mechanism. Retrieves all
* tenants from the {@link SystemManagement#findTenants()} and runs for each
* tenant the {@link RolloutManagement#checkCreatingRollouts(long)} in the
* {@link SystemSecurityContext}.
*/
@Scheduled(initialDelayString = RolloutProperties.PROP_CREATING_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = RolloutProperties.PROP_CREATING_SCHEDULER_DELAY_PLACEHOLDER)
public void creatingRolloutScheduler() {
if (!rolloutProperties.getCreatingScheduler().isEnabled()) {
return;
} }
LOGGER.debug("rollout creating schedule checker has been triggered.");
// run this code in system code privileged to have the necessary
// permission to query and create entities.
systemSecurityContext.runAsSystem(() -> {
// workaround eclipselink that is currently not possible to
// execute a query without multitenancy if MultiTenant
// annotation is used.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for
// each tenant seperately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking creating rollouts for {} tenants", tenants.size());
for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> {
final long fixedDelay = rolloutProperties.getCreatingScheduler().getFixedDelay();
rolloutManagement.checkCreatingRollouts(fixedDelay);
return null;
});
}
return null;
});
}
/**
* Scheduler method called by the spring-async mechanism. Retrieves all
* tenants from the {@link SystemManagement#findTenants()} and runs for each
* tenant the {@link RolloutManagement#checkReadyRollouts(long)} in the
* {@link SystemSecurityContext}. Used to auto start Rollouts as soon as
* their startAt time is reached.
*/
@Scheduled(initialDelayString = RolloutProperties.PROP_READY_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = RolloutProperties.PROP_READY_SCHEDULER_DELAY_PLACEHOLDER)
public void readyRolloutScheduler() {
if (!rolloutProperties.getReadyScheduler().isEnabled()) {
return;
}
LOGGER.debug("rollout ready schedule checker has been triggered.");
// run this code in system code privileged to have the necessary
// permission to query and create entities.
systemSecurityContext.runAsSystem(() -> {
// workaround eclipselink that is currently not possible to
// execute a query without multitenancy if MultiTenant
// annotation is used.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for
// each tenant seperately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking ready rollouts for {} tenants", tenants.size());
for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> {
final long fixedDelay = rolloutProperties.getReadyScheduler().getFixedDelay();
rolloutManagement.checkReadyRollouts(fixedDelay);
return null;
});
}
return null;
});
} }
} }

View File

@@ -71,7 +71,7 @@ public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActi
// scheduled. If the group is empty now, we just finish the group if // scheduled. If the group is empty now, we just finish the group if
// there are not actions available for this group. // there are not actions available for this group.
final List<JpaRolloutGroup> findByRolloutGroupParent = rolloutGroupRepository final List<JpaRolloutGroup> findByRolloutGroupParent = rolloutGroupRepository
.findByParentAndStatus((JpaRolloutGroup) rolloutGroup, RolloutGroupStatus.SCHEDULED); .findByParentIdAndStatus(rolloutGroup.getId(), RolloutGroupStatus.SCHEDULED);
findByRolloutGroupParent.forEach(nextGroup -> { findByRolloutGroupParent.forEach(nextGroup -> {
logger.debug("Rolloutgroup {} is finished, starting next group", nextGroup); logger.debug("Rolloutgroup {} is finished, starting next group", nextGroup);
nextGroup.setStatus(RolloutGroupStatus.FINISHED); nextGroup.setStatus(RolloutGroupStatus.FINISHED);

View File

@@ -20,8 +20,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Target;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
/** /**
@@ -37,25 +37,50 @@ public final class ActionSpecifications {
/** /**
* Specification which joins all necessary tables to retrieve the dependency * Specification which joins all necessary tables to retrieve the dependency
* between a target and a local file assignment through the assigen action * between a target and a local file assignment through the assigned action
* of the target. All actions are included, not only active actions. * of the target. All actions are included, not only active actions.
* *
* @param target * @param controllerId
* the target to verfiy if the given artifact is currently * the target to verify if the given artifact is currently
* assigned or had been assigned * assigned or had been assigned
* @param sha1Hash * @param sha1Hash
* of the local artifact to check wherever the target had ever * of the local artifact to check wherever the target had ever
* been assigned * been assigned
* @return a specification to use with spring JPA * @return a specification to use with spring JPA
*/ */
public static Specification<JpaAction> hasTargetAssignedArtifact(final Target target, final String sha1Hash) { public static Specification<JpaAction> hasTargetAssignedArtifact(final String controllerId, final String sha1Hash) {
return (actionRoot, query, criteriaBuilder) -> { return (actionRoot, query, criteriaBuilder) -> {
final Join<JpaAction, JpaDistributionSet> dsJoin = actionRoot.join(JpaAction_.distributionSet); final Join<JpaAction, JpaDistributionSet> dsJoin = actionRoot.join(JpaAction_.distributionSet);
final SetJoin<JpaDistributionSet, JpaSoftwareModule> modulesJoin = dsJoin.join(JpaDistributionSet_.modules); final SetJoin<JpaDistributionSet, JpaSoftwareModule> modulesJoin = dsJoin.join(JpaDistributionSet_.modules);
final ListJoin<JpaSoftwareModule, JpaArtifact> artifactsJoin = modulesJoin final ListJoin<JpaSoftwareModule, JpaArtifact> artifactsJoin = modulesJoin
.join(JpaSoftwareModule_.artifacts); .join(JpaSoftwareModule_.artifacts);
return criteriaBuilder.and(criteriaBuilder.equal(artifactsJoin.get(JpaArtifact_.sha1Hash), sha1Hash), return criteriaBuilder.and(criteriaBuilder.equal(artifactsJoin.get(JpaArtifact_.sha1Hash), sha1Hash),
criteriaBuilder.equal(actionRoot.get(JpaAction_.target), target)); criteriaBuilder.equal(actionRoot.get(JpaAction_.target).get(JpaTarget_.controllerId),
controllerId));
};
}
/**
* Specification which joins all necessary tables to retrieve the dependency
* between a target and a local file assignment through the assigned action
* of the target. All actions are included, not only active actions.
*
* @param targetId
* the target to verify if the given artifact is currently
* assigned or had been assigned
* @param sha1Hash
* of the local artifact to check wherever the target had ever
* been assigned
* @return a specification to use with spring JPA
*/
public static Specification<JpaAction> hasTargetAssignedArtifact(final Long targetId, final String sha1Hash) {
return (actionRoot, query, criteriaBuilder) -> {
final Join<JpaAction, JpaDistributionSet> dsJoin = actionRoot.join(JpaAction_.distributionSet);
final SetJoin<JpaDistributionSet, JpaSoftwareModule> modulesJoin = dsJoin.join(JpaDistributionSet_.modules);
final ListJoin<JpaSoftwareModule, JpaArtifact> artifactsJoin = modulesJoin
.join(JpaSoftwareModule_.artifacts);
return criteriaBuilder.and(criteriaBuilder.equal(artifactsJoin.get(JpaArtifact_.sha1Hash), sha1Hash),
criteriaBuilder.equal(actionRoot.get(JpaAction_.target).get(JpaTarget_.id), targetId));
}; };
} }
} }

View File

@@ -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.repository.jpa.specifications;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link Rollout}s. The class provides Spring Data
* JPQL Specifications.
*
*/
public final class RolloutSpecification {
private RolloutSpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link Rollout}s by its DELETED
* attribute.
*
* @param isDeleted
* TRUE/FALSE are compared to the attribute DELETED. If NULL the
* attribute is ignored
* @return the {@link Rollout} {@link Specification}
*/
public static Specification<JpaRollout> isDeleted(final Boolean isDeleted) {
return (root, query, cb) -> cb.equal(root.<Boolean> get(JpaRollout_.deleted), isDeleted);
}
}

View File

@@ -0,0 +1,26 @@
ALTER TABLE sp_rollout ADD COLUMN deleted BOOLEAN;
UPDATE sp_rollout SET deleted = 0;
ALTER TABLE sp_action MODIFY target BIGINT NOT NULL;
ALTER TABLE sp_action MODIFY distribution_set BIGINT NOT NULL;
ALTER TABLE sp_action MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_action_status MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_rollout MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_rollout MODIFY distribution_set BIGINT NOT NULL;
ALTER TABLE sp_rolloutgroup MODIFY rollout BIGINT NOT NULL;
ALTER TABLE sp_rolloutgroup MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_ds_type_element DROP CONSTRAINT fk_ds_type_element_element;
ALTER TABLE sp_ds_type_element
ADD CONSTRAINT fk_ds_type_element_element
FOREIGN KEY (distribution_set_type)
REFERENCES sp_distribution_set_type (id)
ON DELETE CASCADE;
ALTER TABLE sp_ds_type_element DROP CONSTRAINT fk_ds_type_element_smtype;
ALTER TABLE sp_ds_type_element
ADD CONSTRAINT fk_ds_type_element_smtype
FOREIGN KEY (software_module_type)
REFERENCES sp_software_module_type (id)
ON DELETE CASCADE;

View File

@@ -0,0 +1,26 @@
ALTER TABLE sp_rollout ADD COLUMN deleted BOOLEAN;
UPDATE sp_rollout SET deleted = 0;
ALTER TABLE sp_action MODIFY target BIGINT NOT NULL;
ALTER TABLE sp_action MODIFY distribution_set BIGINT NOT NULL;
ALTER TABLE sp_action MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_action_status MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_rollout MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_rollout MODIFY distribution_set BIGINT NOT NULL;
ALTER TABLE sp_rolloutgroup MODIFY rollout BIGINT NOT NULL;
ALTER TABLE sp_rolloutgroup MODIFY status INTEGER NOT NULL;
ALTER TABLE sp_ds_type_element DROP FOREIGN KEY fk_ds_type_element_element;
ALTER TABLE sp_ds_type_element
ADD CONSTRAINT fk_ds_type_element_element
FOREIGN KEY (distribution_set_type)
REFERENCES sp_distribution_set_type (id)
ON DELETE CASCADE;
ALTER TABLE sp_ds_type_element DROP FOREIGN KEY fk_ds_type_element_smtype;
ALTER TABLE sp_ds_type_element
ADD CONSTRAINT fk_ds_type_element_smtype
FOREIGN KEY (software_module_type)
REFERENCES sp_software_module_type (id)
ON DELETE CASCADE;

View File

@@ -65,6 +65,12 @@ public class RemoteIdEventTest extends AbstractRemoteEventTest {
assertAndCreateRemoteEvent(SoftwareModuleDeletedEvent.class); assertAndCreateRemoteEvent(SoftwareModuleDeletedEvent.class);
} }
@Test
@Description("Verifies that the rollout id is correct reloaded")
public void testRolloutDeletedEvent() {
assertAndCreateRemoteEvent(RolloutDeletedEvent.class);
}
protected void assertAndCreateRemoteEvent(final Class<? extends RemoteIdEvent> eventType) { protected void assertAndCreateRemoteEvent(final Class<? extends RemoteIdEvent> eventType) {
final Constructor<?> constructor = Arrays.stream(eventType.getDeclaredConstructors()) final Constructor<?> constructor = Arrays.stream(eventType.getDeclaredConstructors())

View File

@@ -13,6 +13,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.junit.Test; import org.junit.Test;
@@ -46,6 +47,7 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
final Target target = testdataFactory.createTarget("Test"); final Target target = testdataFactory.createTarget("Test");
generateAction.setTarget(target); generateAction.setTarget(target);
generateAction.setDistributionSet(dsA); generateAction.setDistributionSet(dsA);
generateAction.setStatus(Status.RUNNING);
final Action action = actionRepository.save(generateAction); final Action action = actionRepository.save(generateAction);
final TargetAssignDistributionSetEvent assignmentEvent = new TargetAssignDistributionSetEvent(action, final TargetAssignDistributionSetEvent assignmentEvent = new TargetAssignDistributionSetEvent(action,

View File

@@ -8,9 +8,16 @@
*/ */
package org.eclipse.hawkbit.repository.event.remote.entity; package org.eclipse.hawkbit.repository.event.remote.entity;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.lang.reflect.Constructor;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.junit.Test; import org.junit.Test;
@@ -35,7 +42,49 @@ public class ActionEventTest extends AbstractRemoteEntityEventTest<Action> {
@Description("Verifies that the action entity reloading by remote updated works") @Description("Verifies that the action entity reloading by remote updated works")
public void testActionUpdatedEvent() { public void testActionUpdatedEvent() {
assertAndCreateRemoteEvent(ActionUpdatedEvent.class); assertAndCreateRemoteEvent(ActionUpdatedEvent.class);
}
@Override
protected RemoteEntityEvent<?> createRemoteEvent(final Action baseEntity,
final Class<? extends RemoteEntityEvent<?>> eventType) {
Constructor<?> constructor = null;
for (final Constructor<?> constructors : eventType.getDeclaredConstructors()) {
if (constructors.getParameterCount() == 4) {
constructor = constructors;
}
}
if (constructor == null) {
throw new IllegalArgumentException("No suitable constructor foundes");
}
try {
return (RemoteEntityEvent<?>) constructor.newInstance(baseEntity, 1L, 2L, "Node");
} catch (final ReflectiveOperationException e) {
fail("Exception should not happen " + e.getMessage());
}
return null;
}
@Override
protected RemoteEntityEvent<?> assertEntity(final Action baseEntity, final RemoteEntityEvent<?> e) {
final AbstractActionEvent event = (AbstractActionEvent) e;
assertThat(event.getEntity()).isSameAs(baseEntity);
assertThat(event.getRolloutId()).isEqualTo(1L);
AbstractActionEvent underTestCreatedEvent = (AbstractActionEvent) createProtoStuffEvent(event);
assertThat(underTestCreatedEvent.getEntity()).isEqualTo(baseEntity);
assertThat(underTestCreatedEvent.getRolloutId()).isEqualTo(1L);
assertThat(underTestCreatedEvent.getRolloutGroupId()).isEqualTo(2L);
underTestCreatedEvent = (AbstractActionEvent) createJacksonEvent(event);
assertThat(underTestCreatedEvent.getEntity()).isEqualTo(baseEntity);
assertThat(underTestCreatedEvent.getRolloutId()).isEqualTo(1L);
assertThat(underTestCreatedEvent.getRolloutGroupId()).isEqualTo(2L);
return underTestCreatedEvent;
} }
@Override @Override
@@ -43,8 +92,10 @@ public class ActionEventTest extends AbstractRemoteEntityEventTest<Action> {
final JpaAction generateAction = new JpaAction(); final JpaAction generateAction = new JpaAction();
generateAction.setActionType(ActionType.FORCED); generateAction.setActionType(ActionType.FORCED);
final Target target = testdataFactory.createTarget("Test"); final Target target = testdataFactory.createTarget("Test");
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
generateAction.setTarget(target); generateAction.setTarget(target);
generateAction.setDistributionSet(testdataFactory.createDistributionSet()); generateAction.setDistributionSet(distributionSet);
generateAction.setStatus(Status.RUNNING);
return actionRepository.save(generateAction); return actionRepository.save(generateAction);
} }

View File

@@ -8,8 +8,10 @@
*/ */
package org.eclipse.hawkbit.repository.event.remote.entity; package org.eclipse.hawkbit.repository.event.remote.entity;
import static org.junit.Assert.fail;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import java.lang.reflect.Constructor;
import java.util.UUID; import java.util.UUID;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -44,6 +46,47 @@ public class RolloutGroupEventTest extends AbstractRemoteEntityEventTest<Rollout
assertAndCreateRemoteEvent(RolloutGroupUpdatedEvent.class); assertAndCreateRemoteEvent(RolloutGroupUpdatedEvent.class);
} }
@Override
protected RemoteEntityEvent<?> createRemoteEvent(final RolloutGroup baseEntity,
final Class<? extends RemoteEntityEvent<?>> eventType) {
Constructor<?> constructor = null;
for (final Constructor<?> constructors : eventType.getDeclaredConstructors()) {
if (constructors.getParameterCount() == 3) {
constructor = constructors;
}
}
if (constructor == null) {
throw new IllegalArgumentException("No suitable constructor foundes");
}
try {
return (RemoteEntityEvent<?>) constructor.newInstance(baseEntity, 1L, "Node");
} catch (final ReflectiveOperationException e) {
fail("Exception should not happen " + e.getMessage());
}
return null;
}
@Override
protected RemoteEntityEvent<?> assertEntity(final RolloutGroup baseEntity, final RemoteEntityEvent<?> e) {
final AbstractRolloutGroupEvent event = (AbstractRolloutGroupEvent) e;
assertThat(event.getEntity()).isSameAs(baseEntity);
assertThat(event.getRolloutId()).isEqualTo(1L);
AbstractRolloutGroupEvent underTestCreatedEvent = (AbstractRolloutGroupEvent) createProtoStuffEvent(event);
assertThat(underTestCreatedEvent.getEntity()).isEqualTo(baseEntity);
assertThat(underTestCreatedEvent.getRolloutId()).isEqualTo(1L);
underTestCreatedEvent = (AbstractRolloutGroupEvent) createJacksonEvent(event);
assertThat(underTestCreatedEvent.getEntity()).isEqualTo(baseEntity);
assertThat(underTestCreatedEvent.getRolloutId()).isEqualTo(1L);
return underTestCreatedEvent;
}
@Override @Override
protected RolloutGroup createEntity() { protected RolloutGroup createEntity() {
testdataFactory.createTarget(UUID.randomUUID().toString()); testdataFactory.createTarget(UUID.randomUUID().toString());

View File

@@ -31,6 +31,8 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import com.google.common.collect.Lists;
@SpringApplicationConfiguration(classes = { @SpringApplicationConfiguration(classes = {
org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration.class }) org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration.class })
public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest { public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest {
@@ -80,6 +82,9 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
@Autowired @Autowired
protected RolloutGroupRepository rolloutGroupRepository; protected RolloutGroupRepository rolloutGroupRepository;
@Autowired
protected RolloutTargetGroupRepository rolloutTargetGroupRepository;
@Autowired @Autowired
protected RolloutRepository rolloutRepository; protected RolloutRepository rolloutRepository;
@@ -91,7 +96,7 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
protected List<Action> findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) { protected List<Action> findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) {
return actionRepository.findByRolloutIdAndStatus(rollout.getId(), actionStatus); return Lists.newArrayList(actionRepository.findByRolloutIdAndStatus(pageReq, rollout.getId(), actionStatus));
} }
protected TargetTagAssignmentResult toggleTagAssignment(final Collection<Target> targets, final TargetTag tag) { protected TargetTagAssignmentResult toggleTagAssignment(final Collection<Target> targets, final TargetTag tag) {

View File

@@ -9,23 +9,29 @@
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException; import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact; import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils; import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils;
import org.eclipse.hawkbit.repository.test.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.Test; import org.junit.Test;
@@ -35,24 +41,47 @@ import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories; import ru.yandex.qatools.allure.annotations.Stories;
/** /**
* Test class for {@link ArtifactManagement} with running MongoDB instance.. * Test class for {@link ArtifactManagement}.
*
*
*
*
*/ */
@Features("Component Tests - Repository") @Features("Component Tests - Repository")
@Stories("Artifact Management") @Stories("Artifact Management")
public class ArtifactManagementTest extends AbstractJpaIntegrationTest { public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
/** @Test
* Test method for @Description("Verifies that management queries react as specfied on calls for non existing entities.")
* {@link org.eclipse.hawkbit.repository.ArtifactManagement#createArtifact(java.io.InputStream)} @ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
* . public void nonExistingEntityQueries() throws URISyntaxException {
* final SoftwareModule module = testdataFactory.createSoftwareModuleOs();
* @throws IOException
* @throws NoSuchAlgorithmException assertThatThrownBy(() -> artifactManagement.createArtifact(IOUtils.toInputStream("test", "UTF-8"), 1234L, "xxx",
*/ null, null, false, null)).isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("SoftwareModule");
assertThatThrownBy(
() -> artifactManagement.createArtifact(IOUtils.toInputStream("test", "UTF-8"), 1234L, "xxx", false))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("SoftwareModule");
assertThatThrownBy(() -> artifactManagement.deleteArtifact(1234L)).isInstanceOf(EntityNotFoundException.class)
.hasMessageContaining("1234").hasMessageContaining("Artifact");
assertThat(artifactManagement.findArtifact(1234L).isPresent()).isFalse();
assertThatThrownBy(() -> artifactManagement.findArtifactBySoftwareModule(pageReq, 1234L))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("SoftwareModule");
assertThat(artifactManagement.findArtifactByFilename("1234").isPresent()).isFalse();
assertThatThrownBy(() -> artifactManagement.findByFilenameAndSoftwareModule("xxx", 1234L))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("SoftwareModule");
assertThat(artifactManagement.findByFilenameAndSoftwareModule("1234", module.getId()).isPresent()).isFalse();
assertThat(artifactManagement.findFirstArtifactBySHA1("1234").isPresent()).isFalse();
assertThat(artifactManagement.loadArtifactBinary("1234").isPresent()).isFalse();
}
@Test @Test
@Description("Test if a local artifact can be created by API including metadata.") @Description("Test if a local artifact can be created by API including metadata.")
public void createArtifact() throws NoSuchAlgorithmException, IOException { public void createArtifact() throws NoSuchAlgorithmException, IOException {
@@ -205,7 +234,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
final Artifact result = artifactManagement.createArtifact(new ByteArrayInputStream(random), final Artifact result = artifactManagement.createArtifact(new ByteArrayInputStream(random),
testdataFactory.createSoftwareModuleOs().getId(), "file1", false); testdataFactory.createSoftwareModuleOs().getId(), "file1", false);
try (InputStream fileInputStream = artifactManagement.loadArtifactBinary(result.getSha1Hash()) try (InputStream fileInputStream = artifactManagement.loadArtifactBinary(result.getSha1Hash()).get()
.getFileInputStream()) { .getFileInputStream()) {
assertTrue("The stored binary matches the given binary", assertTrue("The stored binary matches the given binary",
IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream)); IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream));

View File

@@ -9,9 +9,12 @@
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -29,10 +32,12 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedE
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException; import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.matcher.Expect; import org.eclipse.hawkbit.repository.test.matcher.Expect;
@@ -55,6 +60,75 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Autowired @Autowired
private RepositoryProperties repositoryProperties; private RepositoryProperties repositoryProperties;
@Test
@Description("Verifies that management queries react as specfied on calls for non existing entities.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
public void nonExistingEntityQueries() throws URISyntaxException {
final Target target = testdataFactory.createTarget();
final SoftwareModule module = testdataFactory.createSoftwareModuleOs();
assertThatThrownBy(() -> controllerManagement
.addCancelActionStatus(entityFactory.actionStatus().create(1234L).status(Action.Status.FINISHED)))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Action");
assertThatThrownBy(() -> controllerManagement
.addInformationalActionStatus(entityFactory.actionStatus().create(1234L).status(Action.Status.RUNNING)))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Action");
assertThatThrownBy(() -> controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(1234L).status(Action.Status.FINISHED)))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Action");
assertThat(controllerManagement.findActionWithDetails(1234L).isPresent()).isFalse();
assertThat(controllerManagement.findByControllerId("1234").isPresent()).isFalse();
assertThat(controllerManagement.findByTargetId(1234L).isPresent()).isFalse();
assertThatThrownBy(() -> controllerManagement.findOldestActiveActionByTarget("1234"))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Target");
assertThatThrownBy(() -> controllerManagement
.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), 1234L))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("SoftwareModule");
assertThatThrownBy(
() -> controllerManagement.getActionForDownloadByTargetAndSoftwareModule("1234", module.getId()))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Target");
assertThat(controllerManagement
.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), module.getId()).isPresent())
.isFalse();
assertThatThrownBy(() -> controllerManagement.hasTargetArtifactAssigned(1234L, "XXX"))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Target");
assertThatThrownBy(() -> controllerManagement.hasTargetArtifactAssigned("1234", "XXX"))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Target");
assertThat(controllerManagement.hasTargetArtifactAssigned(target.getControllerId(), "XXX")).isFalse();
assertThat(controllerManagement.hasTargetArtifactAssigned(target.getId(), "XXX")).isFalse();
assertThatThrownBy(() -> controllerManagement.registerRetrieved(1234L, "test message"))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Action");
assertThatThrownBy(() -> controllerManagement.updateControllerAttributes("1234", Maps.newHashMap()))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Target");
assertThatThrownBy(() -> controllerManagement.updateLastTargetQuery("1234", new URI("http://test.com")))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining("1234")
.hasMessageContaining("Target");
}
@Test @Test
@Description("Controller confirms successfull update with FINISHED status.") @Description("Controller confirms successfull update with FINISHED status.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@@ -68,7 +142,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
simulateIntermediateStatusOnUpdate(actionId); simulateIntermediateStatusOnUpdate(actionId);
controllerManagament controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
Action.Status.FINISHED, Action.Status.FINISHED, false); Action.Status.FINISHED, Action.Status.FINISHED, false);
@@ -91,7 +165,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
final Long actionId = createTargetAndAssignDs(); final Long actionId = createTargetAndAssignDs();
deploymentManagement.cancelAction(actionId); deploymentManagement.cancelAction(actionId);
controllerManagament controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
Action.Status.FINISHED, Action.Status.FINISHED, false); Action.Status.FINISHED, Action.Status.FINISHED, false);
@@ -111,7 +185,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
final Long actionId = createTargetAndAssignDs(); final Long actionId = createTargetAndAssignDs();
try { try {
controllerManagament.addCancelActionStatus( controllerManagement.addCancelActionStatus(
entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED)); entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
fail("Expected " + CancelActionNotAllowedException.class.getName()); fail("Expected " + CancelActionNotAllowedException.class.getName());
} catch (final CancelActionNotAllowedException e) { } catch (final CancelActionNotAllowedException e) {
@@ -144,7 +218,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
simulateIntermediateStatusOnCancellation(actionId); simulateIntermediateStatusOnCancellation(actionId);
controllerManagament controllerManagement
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED)); .addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
Action.Status.CANCELED, Action.Status.FINISHED, false); Action.Status.CANCELED, Action.Status.FINISHED, false);
@@ -171,7 +245,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
simulateIntermediateStatusOnCancellation(actionId); simulateIntermediateStatusOnCancellation(actionId);
controllerManagament controllerManagement
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.CANCELED)); .addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.CANCELED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
Action.Status.CANCELED, Action.Status.CANCELED, false); Action.Status.CANCELED, Action.Status.CANCELED, false);
@@ -199,7 +273,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
simulateIntermediateStatusOnCancellation(actionId); simulateIntermediateStatusOnCancellation(actionId);
controllerManagament.addCancelActionStatus( controllerManagement.addCancelActionStatus(
entityFactory.actionStatus().create(actionId).status(Action.Status.CANCEL_REJECTED)); entityFactory.actionStatus().create(actionId).status(Action.Status.CANCEL_REJECTED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.CANCEL_REJECTED, true); Action.Status.RUNNING, Action.Status.CANCEL_REJECTED, true);
@@ -227,7 +301,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
simulateIntermediateStatusOnCancellation(actionId); simulateIntermediateStatusOnCancellation(actionId);
controllerManagament controllerManagement
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR)); .addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.ERROR, true); Action.Status.RUNNING, Action.Status.ERROR, true);
@@ -249,22 +323,22 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Step @Step
private void simulateIntermediateStatusOnCancellation(final Long actionId) { private void simulateIntermediateStatusOnCancellation(final Long actionId) {
controllerManagament controllerManagement
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING)); .addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.RUNNING, true); Action.Status.CANCELING, Action.Status.RUNNING, true);
controllerManagament controllerManagement
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD)); .addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.DOWNLOAD, true); Action.Status.CANCELING, Action.Status.DOWNLOAD, true);
controllerManagament controllerManagement
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED)); .addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.RETRIEVED, true); Action.Status.CANCELING, Action.Status.RETRIEVED, true);
controllerManagament controllerManagement
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING)); .addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.CANCELING, Action.Status.WARNING, true); Action.Status.CANCELING, Action.Status.WARNING, true);
@@ -272,22 +346,22 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
@Step @Step
private void simulateIntermediateStatusOnUpdate(final Long actionId) { private void simulateIntermediateStatusOnUpdate(final Long actionId) {
controllerManagament controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.RUNNING, true); Action.Status.RUNNING, Action.Status.RUNNING, true);
controllerManagament controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.DOWNLOAD, true); Action.Status.RUNNING, Action.Status.DOWNLOAD, true);
controllerManagament controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.RETRIEVED, true); Action.Status.RUNNING, Action.Status.RETRIEVED, true);
controllerManagament controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.WARNING, true); Action.Status.RUNNING, Action.Status.WARNING, true);
@@ -305,7 +379,10 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
final List<ActionStatus> actionStatusList = deploymentManagement.findActionStatusByAction(pageReq, actionId) final List<ActionStatus> actionStatusList = deploymentManagement.findActionStatusByAction(pageReq, actionId)
.getContent(); .getContent();
assertThat(actionStatusList.get(actionStatusList.size() - 1).getStatus()).isEqualTo(expectedActionStatus); assertThat(actionStatusList.get(actionStatusList.size() - 1).getStatus()).isEqualTo(expectedActionStatus);
if (actionActive) {
assertThat(controllerManagement.findOldestActiveActionByTarget(controllerId).get().getId())
.isEqualTo(actionId);
}
} }
@Test @Test
@@ -332,31 +409,31 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(artifact.getSha1Hash()).isEqualTo(artifact2.getSha1Hash()); assertThat(artifact.getSha1Hash()).isEqualTo(artifact2.getSha1Hash());
assertThat( assertThat(
controllerManagament.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact.getSha1Hash())) controllerManagement.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact.getSha1Hash()))
.isFalse(); .isFalse();
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getAssignedEntity().iterator() savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getAssignedEntity().iterator()
.next(); .next();
assertThat( assertThat(
controllerManagament.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact.getSha1Hash())) controllerManagement.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact.getSha1Hash()))
.isTrue(); .isTrue();
assertThat( assertThat(
controllerManagament.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact2.getSha1Hash())) controllerManagement.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact2.getSha1Hash()))
.isTrue(); .isTrue();
} }
@Test @Test
@Description("Register a controller which does not exist") @Description("Register a controller which does not exist")
public void findOrRegisterTargetIfItDoesNotexist() { public void findOrRegisterTargetIfItDoesNotexist() {
final Target target = controllerManagament.findOrRegisterTargetIfItDoesNotexist("AA", null); final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist("AA", null);
assertThat(target).as("target should not be null").isNotNull(); assertThat(target).as("target should not be null").isNotNull();
final Target sameTarget = controllerManagament.findOrRegisterTargetIfItDoesNotexist("AA", null); final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotexist("AA", null);
assertThat(target).as("Target should be the equals").isEqualTo(sameTarget); assertThat(target).as("Target should be the equals").isEqualTo(sameTarget);
assertThat(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L); assertThat(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L);
// throws exception // throws exception
try { try {
controllerManagament.findOrRegisterTargetIfItDoesNotexist("", null); controllerManagement.findOrRegisterTargetIfItDoesNotexist("", null);
fail("should fail as target does not exist"); fail("should fail as target does not exist");
} catch (final ConstraintViolationException e) { } catch (final ConstraintViolationException e) {
@@ -375,19 +452,19 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
final Long actionId = createTargetAndAssignDs(); final Long actionId = createTargetAndAssignDs();
// test and verify // test and verify
controllerManagament controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
Action.Status.RUNNING, Action.Status.RUNNING, true); Action.Status.RUNNING, Action.Status.RUNNING, true);
controllerManagament controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR, assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
Action.Status.ERROR, Action.Status.ERROR, false); Action.Status.ERROR, Action.Status.ERROR, false);
// try with disabled late feedback // try with disabled late feedback
repositoryProperties.setRejectActionStatusForClosedAction(true); repositoryProperties.setRejectActionStatusForClosedAction(true);
controllerManagament controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
// test // test
@@ -397,7 +474,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
// try with enabled late feedback - should not make a difference as it // try with enabled late feedback - should not make a difference as it
// only allows intermediate feedbacks and not multiple close // only allows intermediate feedbacks and not multiple close
repositoryProperties.setRejectActionStatusForClosedAction(false); repositoryProperties.setRejectActionStatusForClosedAction(false);
controllerManagament controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
// test // test
@@ -422,7 +499,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
// try with disabled late feedback // try with disabled late feedback
repositoryProperties.setRejectActionStatusForClosedAction(true); repositoryProperties.setRejectActionStatusForClosedAction(true);
controllerManagament controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
// test // test
@@ -432,7 +509,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
// try with enabled late feedback - should not make a difference as it // try with enabled late feedback - should not make a difference as it
// only allows intermediate feedbacks and not multiple close // only allows intermediate feedbacks and not multiple close
repositoryProperties.setRejectActionStatusForClosedAction(false); repositoryProperties.setRejectActionStatusForClosedAction(false);
controllerManagament controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED)); .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
// test // test
@@ -458,7 +535,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
final Action action = prepareFinishedUpdate(); final Action action = prepareFinishedUpdate();
controllerManagament.addUpdateActionStatus( controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING)); entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING));
// nothing changed as "feedback after close" is disabled // nothing changed as "feedback after close" is disabled
@@ -483,7 +560,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
repositoryProperties.setRejectActionStatusForClosedAction(false); repositoryProperties.setRejectActionStatusForClosedAction(false);
Action action = prepareFinishedUpdate(); Action action = prepareFinishedUpdate();
action = controllerManagament.addUpdateActionStatus( action = controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING)); entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING));
// nothing changed as "feedback after close" is disabled // nothing changed as "feedback after close" is disabled
@@ -512,7 +589,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
private void addAttributeAndVerify(final String controllerId) { private void addAttributeAndVerify(final String controllerId) {
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(1); final Map<String, String> testData = Maps.newHashMapWithExpectedSize(1);
testData.put("test1", "testdata1"); testData.put("test1", "testdata1");
controllerManagament.updateControllerAttributes(controllerId, testData); controllerManagement.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get(); final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get();
assertThat(target.getTargetInfo().getControllerAttributes()).as("Controller Attributes are wrong") assertThat(target.getTargetInfo().getControllerAttributes()).as("Controller Attributes are wrong")
@@ -523,7 +600,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
private void addSecondAttributeAndVerify(final String controllerId) { private void addSecondAttributeAndVerify(final String controllerId) {
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(2); final Map<String, String> testData = Maps.newHashMapWithExpectedSize(2);
testData.put("test2", "testdata20"); testData.put("test2", "testdata20");
controllerManagament.updateControllerAttributes(controllerId, testData); controllerManagement.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get(); final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get();
testData.put("test1", "testdata1"); testData.put("test1", "testdata1");
@@ -536,7 +613,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(2); final Map<String, String> testData = Maps.newHashMapWithExpectedSize(2);
testData.put("test1", "testdata12"); testData.put("test1", "testdata12");
controllerManagament.updateControllerAttributes(controllerId, testData); controllerManagement.updateControllerAttributes(controllerId, testData);
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get(); final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get();
testData.put("test2", "testdata20"); testData.put("test2", "testdata20");

View File

@@ -752,7 +752,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
.as("Installed distribution set of action should be null").isNotNull(); .as("Installed distribution set of action should be null").isNotNull();
final Page<Action> updAct = actionRepository.findByDistributionSetId(pageReq, dsA.getId()); final Page<Action> updAct = actionRepository.findByDistributionSetId(pageReq, dsA.getId());
controllerManagament.addUpdateActionStatus( controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(updAct.getContent().get(0).getId()).status(Status.FINISHED)); entityFactory.actionStatus().create(updAct.getContent().get(0).getId()).status(Status.FINISHED));
targ = targetManagement.findTargetByControllerID(targ.getControllerId()).get(); targ = targetManagement.findTargetByControllerID(targ.getControllerId()).get();

View File

@@ -139,7 +139,7 @@ public class ReportManagementTest extends AbstractJpaIntegrationTest {
final Target createTarget = testdataFactory.createTarget("t" + month); final Target createTarget = testdataFactory.createTarget("t" + month);
final DistributionSetAssignmentResult result = assignDistributionSet(distributionSet, final DistributionSetAssignmentResult result = assignDistributionSet(distributionSet,
Lists.newArrayList(createTarget)); Lists.newArrayList(createTarget));
controllerManagament.registerRetrieved(result.getActions().get(0), controllerManagement.registerRetrieved(result.getActions().get(0),
"Controller retrieved update action and should start now the download."); "Controller retrieved update action and should start now the download.");
} }
DataReportSeries<LocalDate> feedbackReceivedOverTime = reportManagement DataReportSeries<LocalDate> feedbackReceivedOverTime = reportManagement
@@ -160,7 +160,7 @@ public class ReportManagementTest extends AbstractJpaIntegrationTest {
final Target createTarget = testdataFactory.createTarget("t2" + month); final Target createTarget = testdataFactory.createTarget("t2" + month);
final DistributionSetAssignmentResult result = assignDistributionSet(distributionSet, final DistributionSetAssignmentResult result = assignDistributionSet(distributionSet,
Lists.newArrayList(createTarget)); Lists.newArrayList(createTarget));
controllerManagament.registerRetrieved(result.getActions().get(0), controllerManagement.registerRetrieved(result.getActions().get(0),
"Controller retrieved update action and should start now the download."); "Controller retrieved update action and should start now the download.");
} }
feedbackReceivedOverTime = reportManagement.feedbackReceivedOverTime(DateTypes.perMonth(), from, to); feedbackReceivedOverTime = reportManagement.feedbackReceivedOverTime(DateTypes.perMonth(), from, to);

Some files were not shown because too many files have changed in this diff Show More