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>
|
Before Width: | Height: | Size: 495 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 319 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 461 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 319 KiB After Width: | Height: | Size: 87 KiB |
BIN
docs/src/main/resources/documentation/images/ui/rollout-mgmt.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 302 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 232 KiB |
|
Before Width: | Height: | Size: 581 KiB After Width: | Height: | Size: 164 KiB |
@@ -74,4 +74,4 @@ A _Distribution Set_ entity may have for example URIs to artifacts, _Software Mo
|
||||
"metadata": {
|
||||
"href": "http://localhost:8080/rest/v1/softwaremodules/83/metadata?offset=0&limit=50"
|
||||
}
|
||||
{% endhighlight %}
|
||||
{% endhighlight %}
|
||||
@@ -83,8 +83,8 @@ Allows to:
|
||||
Software rollout in large scale, rollout status overview and rollout management.
|
||||
|
||||
## Features explained
|
||||
- Create, update and start of rollouts.
|
||||
- Pause and resume of rollouts.
|
||||
- Create, update, copy and delete of rollouts.
|
||||
- Start, pause and resume of rollouts.
|
||||
- Progress monitoring for the entire rollout and the individual groups.
|
||||
- Drill down to see the groups in a rollout and targets in each group.
|
||||
- Rollout attributes:
|
||||
|
||||
@@ -153,6 +153,7 @@ public class DeviceSimulatorUpdater {
|
||||
if (device.getProgress() <= 0 && modules != null) {
|
||||
device.setUpdateStatus(simulateDownloads(device.getTargetSecurityToken()));
|
||||
if (isErrorResponse(device.getUpdateStatus())) {
|
||||
device.setProgress(1.0);
|
||||
callback.updateFinished(device, actionId);
|
||||
eventbus.post(new ProgressUpdate(device));
|
||||
return;
|
||||
@@ -216,8 +217,11 @@ public class DeviceSimulatorUpdater {
|
||||
|
||||
private static UpdateStatus downloadUrl(final String url, final String targetToken, final String sha1Hash,
|
||||
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 {
|
||||
return readAndCheckDownloadUrl(url, targetToken, sha1Hash, size);
|
||||
|
||||
@@ -232,7 +232,7 @@ public class SpSenderService extends SenderService {
|
||||
headers.put(MessageHeaderKey.TENANT, tenant);
|
||||
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
||||
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
|
||||
actionUpdateStatus.getMessage().addAll(updateResultMessages);
|
||||
actionUpdateStatus.addMessage(updateResultMessages);
|
||||
|
||||
actionUpdateStatus.setActionId(actionId);
|
||||
return convertMessage(actionUpdateStatus, messageProperties);
|
||||
|
||||
@@ -17,16 +17,16 @@ spring.datasource.username=root
|
||||
spring.datasource.password=
|
||||
spring.datasource.driverClassName=org.mariadb.jdbc.Driver
|
||||
|
||||
spring.datasource.max-active=100
|
||||
spring.datasource.max-idle=10
|
||||
spring.datasource.min-idle=10
|
||||
spring.datasource.initial-size=10
|
||||
spring.datasource.validation-query=select 1 from dual
|
||||
spring.datasource.validation-interval=30000
|
||||
spring.datasource.test-on-borrow=true
|
||||
spring.datasource.test-on-return=false
|
||||
spring.datasource.test-while-idle=true
|
||||
spring.datasource.time-between-eviction-runs-millis=30000
|
||||
spring.datasource.min-evictable-idle-time-millis=60000
|
||||
spring.datasource.max-wait=10000
|
||||
spring.datasource.jmx-enabled=true
|
||||
spring.datasource.tomcat.max-active=100
|
||||
spring.datasource.tomcat.max-idle=10
|
||||
spring.datasource.tomcat.min-idle=10
|
||||
spring.datasource.tomcat.initial-size=10
|
||||
spring.datasource.tomcat.validation-query=select 1 from dual
|
||||
spring.datasource.tomcat.validation-interval=30000
|
||||
spring.datasource.tomcat.test-on-borrow=true
|
||||
spring.datasource.tomcat.test-on-return=false
|
||||
spring.datasource.tomcat.test-while-idle=true
|
||||
spring.datasource.tomcat.time-between-eviction-runs-millis=30000
|
||||
spring.datasource.tomcat.min-evictable-idle-time-millis=60000
|
||||
spring.datasource.tomcat.max-wait=10000
|
||||
spring.datasource.tomcat.jmx-enabled=true
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@@ -123,7 +124,6 @@ public class ConfigurableScenario {
|
||||
.createTargetTags(new TagBuilder().name(group).description("Group " + group).build()).getBody()
|
||||
.get(0).getTagId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
private void cleanRepository() {
|
||||
@@ -136,11 +136,20 @@ public class ConfigurableScenario {
|
||||
}
|
||||
|
||||
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() {
|
||||
LOGGER.info("Delete SoftwareModules");
|
||||
PagedList<MgmtSoftwareModule> modules;
|
||||
do {
|
||||
modules = softwareModuleResource.getSoftwareModules(0, PAGE_SIZE, null, null).getBody();
|
||||
@@ -150,6 +159,8 @@ public class ConfigurableScenario {
|
||||
}
|
||||
|
||||
private void deleteDistributionSets() {
|
||||
LOGGER.info("Delete DistributionSets");
|
||||
|
||||
PagedList<MgmtDistributionSet> distributionSets;
|
||||
do {
|
||||
distributionSets = distributionSetResource.getDistributionSets(0, PAGE_SIZE, null, null).getBody();
|
||||
@@ -170,6 +181,8 @@ public class ConfigurableScenario {
|
||||
}
|
||||
|
||||
private void deleteTargets() {
|
||||
LOGGER.info("Delete Targets");
|
||||
|
||||
PagedList<MgmtTarget> targets;
|
||||
do {
|
||||
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());
|
||||
// create a Rollout
|
||||
final MgmtRolloutResponseBody rolloutResponseBody = rolloutResource.create(new RolloutBuilder()
|
||||
.name("Rollout" + set.getName() + set.getVersion()).semiAutomaticGroups(createRolloutGroups(scenario))
|
||||
.targetFilterQuery("name==*").distributionSetId(set.getDsId())
|
||||
.name("SemiAutomaticRollout" + set.getName() + set.getVersion())
|
||||
.semiAutomaticGroups(createRolloutGroups(scenario)).targetFilterQuery("name==*")
|
||||
.distributionSetId(set.getDsId())
|
||||
.successThreshold(String.valueOf(scenario.getRolloutSuccessThreshold())).errorThreshold("5").build())
|
||||
.getBody();
|
||||
|
||||
@@ -218,13 +232,15 @@ public class ConfigurableScenario {
|
||||
}
|
||||
|
||||
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 -> {
|
||||
result.add(createGroup(1, groupname, 10F));
|
||||
result.add(createGroup(2, groupname, 50F));
|
||||
result.add(createGroup(3, groupname, 100F));
|
||||
});
|
||||
result.add(createFinalGroup());
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -238,6 +254,15 @@ public class ConfigurableScenario {
|
||||
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) {
|
||||
LOGGER.info("Run rollout for set {}", set.getDsId());
|
||||
// create a Rollout
|
||||
@@ -256,6 +281,17 @@ public class ConfigurableScenario {
|
||||
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) {
|
||||
do {
|
||||
try {
|
||||
|
||||
@@ -67,7 +67,7 @@ public class MgmtUiAutoConfiguration {
|
||||
* UI has an own strategy.
|
||||
*
|
||||
* @param applicationContext
|
||||
* the context to add the listener
|
||||
* the context to add the listener to
|
||||
* @param executorService
|
||||
* the general scheduler service
|
||||
* @param eventBus
|
||||
|
||||
@@ -16,6 +16,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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.
|
||||
@@ -36,4 +38,9 @@ public class JpaRepositoryAutoConfiguration {
|
||||
return new VirtualPropertyResolver();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public LockRegistry lockRegistry() {
|
||||
return new DefaultLockRegistry();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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 DdiControllerBase result = new DdiControllerBase(
|
||||
new DdiConfig(new DdiPolling(defaultControllerPollTime)));
|
||||
|
||||
if (action.isPresent()) {
|
||||
if (action.get().isCancelingOrCanceled()) {
|
||||
if (action != null) {
|
||||
if (action.isCancelingOrCanceled()) {
|
||||
result.add(linkTo(
|
||||
methodOn(DdiRootController.class, tenantAware.getCurrentTenant()).getControllerCancelAction(
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), action.get().getId()))
|
||||
tenantAware.getCurrentTenant(), target.getControllerId(), action.getId()))
|
||||
.withRel(DdiRestConstants.CANCEL_ACTION));
|
||||
} else {
|
||||
// we need to add the hashcode here of the actionWithStatus
|
||||
@@ -120,7 +119,7 @@ public final class DataConversionHelper {
|
||||
// response because of eTags.
|
||||
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
||||
.getControllerBasedeploymentAction(tenantAware.getCurrentTenant(), target.getControllerId(),
|
||||
action.get().getId(), calculateEtag(action.get())))
|
||||
action.getId(), calculateEtag(action)))
|
||||
.withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.exception.ArtifactBinaryNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.SoftwareModuleNotAssignedToTargetException;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
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())) {
|
||||
result = new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
|
||||
} 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
|
||||
// targetid, i.e. authenticated and not anonymous
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.SoftwareManagement;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
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.SoftwareModuleNotAssignedToTargetException;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
@@ -130,7 +131,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(controllerId, IpUtil
|
||||
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
|
||||
return new ResponseEntity<>(DataConversionHelper.fromTarget(target,
|
||||
controllerManagement.findOldestActiveActionByTarget(controllerId),
|
||||
controllerManagement.findOldestActiveActionByTarget(controllerId).orElse(null),
|
||||
controllerManagement.getPollingTime(), tenantAware), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@@ -156,7 +157,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
|
||||
@SuppressWarnings("squid:S3655")
|
||||
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");
|
||||
if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) {
|
||||
|
||||
@@ -65,7 +65,7 @@ public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
||||
|
||||
savedTarget.getTargetInfo().getControllerAttributes().put("dsafsdf", "sdsds");
|
||||
|
||||
final Target updateControllerAttributes = controllerManagament.updateControllerAttributes(
|
||||
final Target updateControllerAttributes = controllerManagement.updateControllerAttributes(
|
||||
savedTarget.getControllerId(), savedTarget.getTargetInfo().getControllerAttributes());
|
||||
// request controller attributes need to be false because we don't want
|
||||
// to request the
|
||||
|
||||
@@ -201,10 +201,9 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
|
||||
|
||||
checkIfArtifactIsAssignedToTarget(secruityToken, sha1Hash);
|
||||
|
||||
final Artifact artifact = convertDbArtifact(artifactManagement.loadArtifactBinary(sha1Hash));
|
||||
if (artifact == null) {
|
||||
throw new EntityNotFoundException();
|
||||
}
|
||||
final Artifact artifact = convertDbArtifact(artifactManagement.loadArtifactBinary(sha1Hash).orElseThrow(
|
||||
() -> new EntityNotFoundException(org.eclipse.hawkbit.repository.model.Artifact.class, sha1Hash)));
|
||||
|
||||
authentificationResponse.setArtifact(artifact);
|
||||
final String downloadId = UUID.randomUUID().toString();
|
||||
// SHA1 key is set, download by SHA1
|
||||
|
||||
@@ -33,6 +33,8 @@ import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignment
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
@@ -50,6 +52,8 @@ import org.springframework.context.event.EventListener;
|
||||
*/
|
||||
public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AmqpMessageDispatcherService.class);
|
||||
|
||||
private final ArtifactUrlHandler artifactUrlHandler;
|
||||
private final AmqpSenderService amqpSenderService;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
@@ -102,6 +106,9 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.debug("targetAssignDistributionSet retrieved for controller {}. I will forward it to DMF broker.",
|
||||
assignedEvent.getControllerId());
|
||||
|
||||
sendUpdateMessageToTarget(assignedEvent.getTenant(),
|
||||
targetManagement.findTargetByControllerID(assignedEvent.getControllerId()).get(),
|
||||
assignedEvent.getActionId(), assignedEvent.getModules());
|
||||
|
||||
@@ -163,7 +163,7 @@ public class AmqpControllerAuthenticationTest {
|
||||
final DbArtifact artifact = new DbArtifact();
|
||||
artifact.setSize(ARTIFACT_SIZE);
|
||||
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,
|
||||
mock(AmqpMessageDispatcherService.class), controllerManagementMock, new JpaEntityFactory());
|
||||
|
||||
@@ -373,7 +373,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
when(artifactManagementMock.findFirstArtifactBySHA1(SHA1)).thenReturn(Optional.of(localArtifactMock));
|
||||
when(controllerManagementMock.hasTargetArtifactAssigned(securityToken.getControllerId(), SHA1))
|
||||
.thenReturn(true);
|
||||
when(artifactManagementMock.loadArtifactBinary(anyString())).thenReturn(dbArtifactMock);
|
||||
when(artifactManagementMock.loadArtifactBinary(anyString())).thenReturn(Optional.of(dbArtifactMock));
|
||||
when(dbArtifactMock.getArtifactId()).thenReturn("artifactId");
|
||||
when(dbArtifactMock.getSize()).thenReturn(1L);
|
||||
when(dbArtifactMock.getHashes()).thenReturn(new DbArtifactHash(SHA1, "md5"));
|
||||
|
||||
@@ -105,6 +105,18 @@ public interface MgmtRolloutRestApi {
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDownloadArtifactRestApi;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
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.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
@@ -67,7 +68,8 @@ public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi
|
||||
}
|
||||
|
||||
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 String ifMatch = request.getHeader("If-Match");
|
||||
if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) {
|
||||
|
||||
@@ -80,9 +80,9 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
|
||||
|
||||
final Page<Rollout> findModulesAll;
|
||||
if (rsqlParam != null) {
|
||||
findModulesAll = this.rolloutManagement.findAllByPredicate(rsqlParam, pageable);
|
||||
findModulesAll = this.rolloutManagement.findAllByPredicate(rsqlParam, pageable, false);
|
||||
} else {
|
||||
findModulesAll = this.rolloutManagement.findAll(pageable);
|
||||
findModulesAll = this.rolloutManagement.findAll(pageable, false);
|
||||
}
|
||||
|
||||
final List<MgmtRolloutResponseBody> rest = MgmtRolloutMapper.toResponseRollout(findModulesAll.getContent());
|
||||
@@ -140,6 +140,12 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> delete(@PathVariable("rolloutId") final Long rolloutId) {
|
||||
this.rolloutManagement.deleteRollout(rolloutId);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> resume(@PathVariable("rolloutId") final Long rolloutId) {
|
||||
this.rolloutManagement.resumeRollout(rolloutId);
|
||||
|
||||
@@ -16,6 +16,7 @@ import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
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.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
@@ -238,7 +239,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
|
||||
@Step
|
||||
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))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
@@ -272,7 +273,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
|
||||
@Step
|
||||
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))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
@@ -330,7 +331,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
postRollout("rollout2", 5, dsA.getId(), "id==target-0001*", 10);
|
||||
|
||||
// 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())
|
||||
.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);
|
||||
|
||||
// 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))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
@@ -441,7 +442,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
.andExpect(jsonPath("status", equalTo("starting")));
|
||||
|
||||
// Run here, because scheduler is disabled during tests
|
||||
rolloutManagement.checkStartingRollouts(0);
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
// check rollout is in running state
|
||||
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());
|
||||
|
||||
// Run here, because scheduler is disabled during tests
|
||||
rolloutManagement.checkStartingRollouts(0);
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
// pausing rollout
|
||||
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());
|
||||
|
||||
// Run here, because scheduler is disabled during tests
|
||||
rolloutManagement.checkStartingRollouts(0);
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
// pausing rollout
|
||||
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());
|
||||
|
||||
// Run here, because scheduler is disabled during tests
|
||||
rolloutManagement.checkStartingRollouts(0);
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
// starting rollout - already started should lead into bad request
|
||||
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());
|
||||
|
||||
// Run here, because scheduler is disabled during tests
|
||||
rolloutManagement.checkStartingRollouts(0);
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
// retrieve rollout groups from created rollout - 2 groups exists
|
||||
// (amountTargets / groupSize = 2)
|
||||
@@ -615,7 +616,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
private void retrieveAndVerifyRolloutGroupInRunningAndScheduled(final Rollout rollout,
|
||||
final RolloutGroup firstGroup, final RolloutGroup secondGroup) throws Exception {
|
||||
rolloutManagement.startRollout(rollout.getId());
|
||||
rolloutManagement.checkStartingRollouts(0);
|
||||
rolloutManagement.handleRollouts();
|
||||
mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups/{groupId}", rollout.getId(), firstGroup.getId())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
|
||||
@@ -642,7 +643,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
@Step
|
||||
private void retrieveAndVerifyRolloutGroupInReady(final Rollout rollout, final RolloutGroup firstGroup)
|
||||
throws Exception {
|
||||
rolloutManagement.checkCreatingRollouts(0);
|
||||
rolloutManagement.handleRollouts();
|
||||
mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups/{groupId}", rollout.getId(), firstGroup.getId())
|
||||
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
|
||||
@@ -748,7 +749,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
rolloutManagement.startRollout(rollout.getId());
|
||||
|
||||
// Run here, because scheduler is disabled during tests
|
||||
rolloutManagement.checkStartingRollouts(0);
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
final RolloutGroup firstGroup = rolloutGroupManagement
|
||||
.findRolloutGroupsByRolloutId(rollout.getId(), new PageRequest(0, 1, Direction.ASC, "id")).getContent()
|
||||
@@ -779,12 +780,29 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
.andExpect(status().isOk());
|
||||
|
||||
// Run here, because scheduler is disabled during tests
|
||||
rolloutManagement.checkStartingRollouts(0);
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
// check if running
|
||||
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
|
||||
@Description("Testing that rollout paged list with rsql parameter")
|
||||
public void getRolloutWithRSQLParam() throws Exception {
|
||||
@@ -922,9 +940,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
.andExpect(
|
||||
jsonPath("$._links.resume.href", allOf(startsWith(HREF_ROLLOUT_PREFIX), endsWith("/resume"))))
|
||||
.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,
|
||||
@@ -935,16 +951,13 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
||||
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build());
|
||||
|
||||
// Run here, because Scheduler is disabled during tests
|
||||
rolloutManagement.fillRolloutGroupsWithTargets(rollout.getId());
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
return rolloutManagement.findRolloutById(rollout.getId()).get();
|
||||
}
|
||||
|
||||
protected boolean success(final Rollout result) {
|
||||
if (null != result && result.getStatus() == RolloutStatus.RUNNING) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return result != null && result.getStatus() == RolloutStatus.RUNNING;
|
||||
}
|
||||
|
||||
public Rollout getRollout(final Long rolloutId) throws Exception {
|
||||
|
||||
@@ -162,7 +162,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
||||
try (InputStream fileInputStream = artifactManagement
|
||||
.loadArtifactBinary(
|
||||
softwareManagement.findSoftwareModuleById(sm.getId()).get().getArtifacts().get(0).getSha1Hash())
|
||||
.getFileInputStream()) {
|
||||
.get().getFileInputStream()) {
|
||||
assertTrue("Wrong artifact content",
|
||||
IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream));
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
|
||||
final int limitSize = 2;
|
||||
final String knownTargetId = "targetId";
|
||||
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
|
||||
controllerManagament.addUpdateActionStatus(
|
||||
controllerManagement.addUpdateActionStatus(
|
||||
entityFactory.actionStatus().create(actions.get(0).getId()).status(Status.FINISHED).message("test"));
|
||||
|
||||
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("b", "2");
|
||||
testdataFactory.createTarget(knownTargetId);
|
||||
controllerManagament.updateControllerAttributes(knownTargetId, knownControllerAttrs);
|
||||
controllerManagement.updateControllerAttributes(knownTargetId, knownControllerAttrs);
|
||||
|
||||
// test query target over rest resource
|
||||
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) {
|
||||
targetManagement.createTarget(entityFactory.target().create().controllerId(controllerId).name(name)
|
||||
.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++) {
|
||||
final String str = String.valueOf(character);
|
||||
targetManagement.createTarget(entityFactory.target().create().controllerId(str).name(str).description(str));
|
||||
controllerManagament.updateLastTargetQuery(str, null);
|
||||
controllerManagement.updateLastTargetQuery(str, null);
|
||||
character++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
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.ArtifactUploadFailedException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
@@ -58,7 +57,10 @@ public interface ArtifactManagement {
|
||||
*
|
||||
* @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)
|
||||
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);
|
||||
|
||||
/**
|
||||
* Garbage collects local artifact binary file if only referenced by given
|
||||
* {@link Artifact} metadata object.
|
||||
* Garbage collects artifact binaries if only referenced by given
|
||||
* {@link SoftwareModule#getId()} or {@link SoftwareModules} that are marged
|
||||
* as deleted.
|
||||
*
|
||||
*
|
||||
* @param artifactId
|
||||
* the related local artifact
|
||||
* @param artifactSha1Hash
|
||||
* no longer needed
|
||||
* @param moduleId
|
||||
* the garbage colelction call is made for
|
||||
*
|
||||
* @return <code>true</code> if an binary was actually garbage collected
|
||||
*/
|
||||
@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.
|
||||
@@ -144,6 +150,9 @@ public interface ArtifactManagement {
|
||||
* @param softwareModuleId
|
||||
* software module id.
|
||||
* @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
|
||||
+ SpringEvalExpressions.IS_CONTROLLER)
|
||||
@@ -179,6 +188,9 @@ public interface ArtifactManagement {
|
||||
* @param swId
|
||||
* software module id
|
||||
* @return Page<Artifact>
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if software module with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
Page<Artifact> findArtifactBySoftwareModule(@NotNull Pageable pageReq, @NotNull Long swId);
|
||||
@@ -190,11 +202,9 @@ public interface ArtifactManagement {
|
||||
* to search for
|
||||
* @return loaded {@link DbArtifact}
|
||||
*
|
||||
* @throws ArtifactBinaryNotFoundException
|
||||
* if file could not be found in store
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.HAS_CONTROLLER_DOWNLOAD)
|
||||
DbArtifact loadArtifactBinary(@NotEmpty String sha1Hash);
|
||||
Optional<DbArtifact> loadArtifactBinary(@NotEmpty String sha1Hash);
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -120,6 +120,9 @@ public interface ControllerManagement {
|
||||
* @param controllerId
|
||||
* identifies the target to retrieve the actions from
|
||||
* @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)
|
||||
Optional<Action> findOldestActiveActionByTarget(@NotNull String controllerId);
|
||||
@@ -157,6 +160,9 @@ public interface ControllerManagement {
|
||||
* of the the {@link SoftwareModule} that should be assigned to
|
||||
* the target
|
||||
* @return last {@link Action} for given combination
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if target with given ID does not exist
|
||||
*
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
@@ -184,6 +190,9 @@ public interface ControllerManagement {
|
||||
* @return {@code true} if the given target has currently or had ever a
|
||||
* relation to the given artifact through the action history,
|
||||
* otherwise {@code false}
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
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
|
||||
* relation to the given artifact through the action history,
|
||||
* otherwise {@code false}
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
boolean hasTargetArtifactAssigned(@NotNull Long targetId, @NotEmpty String sha1Hash);
|
||||
|
||||
@@ -149,6 +149,8 @@ public interface DeploymentManagement {
|
||||
* given {@code fieldNameProvider}
|
||||
* @throws RSQLParameterSyntaxException
|
||||
* if the RSQL syntax is wrong
|
||||
* @throws EntityNotFoundException
|
||||
* if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
Long countActionsByTarget(@NotNull String rsqlParam, @NotEmpty String controllerId);
|
||||
@@ -171,6 +173,9 @@ public interface DeploymentManagement {
|
||||
* @param controllerId
|
||||
* the target associated to the actions to count
|
||||
* @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)
|
||||
Long countActionsByTarget(@NotEmpty String controllerId);
|
||||
@@ -309,6 +314,9 @@ public interface DeploymentManagement {
|
||||
* @param controllerId
|
||||
* the target associated with the actions
|
||||
* @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)
|
||||
List<Action> findActiveActionsByTarget(@NotEmpty String controllerId);
|
||||
@@ -320,6 +328,9 @@ public interface DeploymentManagement {
|
||||
* @param controllerId
|
||||
* the target associated with the actions
|
||||
* @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)
|
||||
List<Action> findInActiveActionsByTarget(@NotEmpty String controllerId);
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionStatus;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@@ -40,6 +39,9 @@ public interface RolloutGroupManagement {
|
||||
* @param pageable
|
||||
* the page request to sort and limit the result
|
||||
* @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)
|
||||
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
|
||||
* the result list of the rollout-group.
|
||||
*
|
||||
* @param pageRequest
|
||||
* @param pageable
|
||||
* the page request to sort and limit the result
|
||||
* @param rolloutGroupId
|
||||
* rollout group
|
||||
* @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)
|
||||
Page<TargetWithActionStatus> findAllTargetsWithActionStatus(@NotNull PageRequest pageRequest,
|
||||
Page<TargetWithActionStatus> findAllTargetsWithActionStatus(@NotNull Pageable pageable,
|
||||
@NotNull Long rolloutGroupId);
|
||||
|
||||
/**
|
||||
@@ -165,6 +169,9 @@ public interface RolloutGroupManagement {
|
||||
* @param rolloutGroupId
|
||||
* the rollout group id for the 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)
|
||||
Long countTargetsOfRolloutsGroup(@NotNull Long rolloutGroupId);
|
||||
|
||||
@@ -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.RolloutGroupConditions;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -44,72 +45,37 @@ import org.springframework.util.concurrent.ListenableFuture;
|
||||
public interface RolloutManagement {
|
||||
|
||||
/**
|
||||
* Checking running rollouts. Rollouts which are checked updating the
|
||||
* lastCheck to indicate that the current instance is handling the specific
|
||||
* rollout. This code should run as system-code.
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* SystemSecurityContext.runAsSystem(new Callable<Void>() {
|
||||
* public Void call() throws Exception {
|
||||
* //run system-code
|
||||
* }
|
||||
* });
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* This method is intended to be called by a scheduler. And must be running
|
||||
* in an transaction so it's splitted from the scheduler.
|
||||
*
|
||||
* Rollouts which are currently running are investigated, by means the
|
||||
* error- and finish condition of running groups in this rollout are
|
||||
* evaluated.
|
||||
*
|
||||
* @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).
|
||||
* Process rollout based on its current {@link Rollout#getStatus()}.
|
||||
*
|
||||
* For {@link RolloutStatus#CREATING} that means creating the
|
||||
* {@link RolloutGroup}s with {@link Target}s and when finished switch to
|
||||
* {@link RolloutStatus#READY}.
|
||||
*
|
||||
* For {@link RolloutStatus#READY} that means switching to
|
||||
* {@link RolloutStatus#STARTING} if the {@link Rollout#getStartAt()} is set
|
||||
* 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
|
||||
* {@link RolloutGroup}s in line and when finished switch to
|
||||
* {@link RolloutStatus#RUNNING}.
|
||||
*
|
||||
* For {@link RolloutStatus#RUNNING} that means checking to activate further
|
||||
* groups based on the defined thresholds. Switched to
|
||||
* {@link RolloutStatus#FINISHED} is all groups are finished.
|
||||
*
|
||||
* For {@link RolloutStatus#DELETING} that means either soft delete in case
|
||||
* rollout was already {@link RolloutStatus#RUNNING} which results in status
|
||||
* change {@link RolloutStatus#DELETED} or hard delete from the persistence
|
||||
* otherwise.
|
||||
*
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||
void checkRunningRollouts(long delayBetweenChecks);
|
||||
void handleRollouts();
|
||||
|
||||
/**
|
||||
* Checking Rollouts that are currently being created with asynchronous
|
||||
* assignment of targets to the Rollout Groups.
|
||||
*
|
||||
* @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.
|
||||
* Counts all {@link Rollout}s in the repository that are not marked as
|
||||
* deleted.
|
||||
*
|
||||
* @return number of roll outs
|
||||
*/
|
||||
@@ -212,44 +178,30 @@ public interface RolloutManagement {
|
||||
ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(List<RolloutGroupCreate> groups,
|
||||
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.
|
||||
*
|
||||
* @param pageable
|
||||
* the page request to sort and limit the result
|
||||
* @param deleted
|
||||
* flag if deleted rollouts should be included
|
||||
* @return a page of found rollouts
|
||||
*/
|
||||
@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.
|
||||
*
|
||||
* @param pageable
|
||||
* 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
|
||||
* statuses
|
||||
*
|
||||
*/
|
||||
@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.
|
||||
@@ -258,6 +210,8 @@ public interface RolloutManagement {
|
||||
* the specification to filter rollouts
|
||||
* @param pageable
|
||||
* the page request to sort and limit the result
|
||||
* @param deleted
|
||||
* flag if deleted rollouts should be included
|
||||
* @return a page of found rollouts
|
||||
*
|
||||
* @throws RSQLParameterUnsupportedFieldException
|
||||
@@ -267,7 +221,7 @@ public interface RolloutManagement {
|
||||
* if the RSQL syntax is wrong
|
||||
*/
|
||||
@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.
|
||||
@@ -276,11 +230,14 @@ public interface RolloutManagement {
|
||||
* the page request to sort and limit the result
|
||||
* @param searchText
|
||||
* 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
|
||||
* not exists
|
||||
*/
|
||||
@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.
|
||||
@@ -309,6 +266,8 @@ public interface RolloutManagement {
|
||||
*
|
||||
* @param rolloutId
|
||||
* rollout id
|
||||
* @param deleted
|
||||
* flag if deleted rollouts should be included
|
||||
* @return rollout details of targets count for different statuses
|
||||
*
|
||||
*
|
||||
@@ -417,4 +376,15 @@ public interface RolloutManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,8 +13,8 @@ import org.eclipse.hawkbit.repository.model.Action;
|
||||
/**
|
||||
* Defines the remote event of creating a new {@link Action}.
|
||||
*/
|
||||
public class ActionCreatedEvent extends RemoteEntityEvent<Action> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public class ActionCreatedEvent extends AbstractActionEvent {
|
||||
private static final long serialVersionUID = 2L;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
@@ -28,11 +28,16 @@ public class ActionCreatedEvent extends RemoteEntityEvent<Action> {
|
||||
*
|
||||
* @param action
|
||||
* the created action
|
||||
* @param rolloutId
|
||||
* rollout identifier (optional)
|
||||
* @param rolloutGroupId
|
||||
* rollout group identifier (optional)
|
||||
* @param applicationId
|
||||
* the origin application id
|
||||
*/
|
||||
public ActionCreatedEvent(final Action action, final String applicationId) {
|
||||
super(action, applicationId);
|
||||
public ActionCreatedEvent(final Action action, final Long rolloutId, final Long rolloutGroupId,
|
||||
final String applicationId) {
|
||||
super(action, rolloutId, rolloutGroupId, applicationId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import org.eclipse.hawkbit.repository.model.Action;
|
||||
/**
|
||||
* Defines the remote event of updated a {@link Action}.
|
||||
*/
|
||||
public class ActionUpdatedEvent extends RemoteEntityEvent<Action> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public class ActionUpdatedEvent extends AbstractActionEvent {
|
||||
private static final long serialVersionUID = 2L;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
@@ -28,11 +28,16 @@ public class ActionUpdatedEvent extends RemoteEntityEvent<Action> {
|
||||
*
|
||||
* @param action
|
||||
* the updated action
|
||||
* @param rolloutId
|
||||
* rollout identifier (optional)
|
||||
* @param rolloutGroupId
|
||||
* rollout group identifier (optional)
|
||||
* @param applicationId
|
||||
* the origin application id
|
||||
*/
|
||||
public ActionUpdatedEvent(final Action action, final String applicationId) {
|
||||
super(action, applicationId);
|
||||
public ActionUpdatedEvent(final Action action, final Long rolloutId, final Long rolloutGroupId,
|
||||
final String applicationId) {
|
||||
super(action, rolloutId, rolloutGroupId, applicationId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,12 +15,9 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
* 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 Long rolloutId;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
@@ -33,16 +30,12 @@ public class RolloutGroupCreatedEvent extends RemoteEntityEvent<RolloutGroup> {
|
||||
*
|
||||
* @param rolloutGroup
|
||||
* the updated rolloutGroup
|
||||
* @param rolloutId
|
||||
* of the related rollout
|
||||
* @param applicationId
|
||||
* the origin application id
|
||||
*/
|
||||
public RolloutGroupCreatedEvent(final RolloutGroup rolloutGroup, final String applicationId) {
|
||||
super(rolloutGroup, applicationId);
|
||||
this.rolloutId = rolloutGroup.getRollout().getId();
|
||||
public RolloutGroupCreatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId, final String applicationId) {
|
||||
super(rolloutGroup, rolloutId, applicationId);
|
||||
}
|
||||
|
||||
public Long getRolloutId() {
|
||||
return rolloutId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ import org.eclipse.hawkbit.repository.model.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.
|
||||
@@ -29,11 +29,13 @@ public class RolloutGroupUpdatedEvent extends RemoteEntityEvent<RolloutGroup> {
|
||||
*
|
||||
* @param rolloutGroup
|
||||
* the updated rolloutGroup
|
||||
* @param rolloutId
|
||||
* of the related rollout
|
||||
* @param applicationId
|
||||
* the origin application id
|
||||
*/
|
||||
public RolloutGroupUpdatedEvent(final RolloutGroup rolloutGroup, final String applicationId) {
|
||||
super(rolloutGroup, applicationId);
|
||||
public RolloutGroupUpdatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId, final String applicationId) {
|
||||
super(rolloutGroup, rolloutId, applicationId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,9 +18,6 @@ import org.eclipse.hawkbit.exception.SpServerError;
|
||||
*/
|
||||
public final class ArtifactUploadFailedException extends AbstractServerRtException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,10 @@ import org.springframework.hateoas.Identifiable;
|
||||
*/
|
||||
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}
|
||||
* was created.
|
||||
|
||||
@@ -26,6 +26,12 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus.Status;
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -60,11 +66,11 @@ public interface Rollout extends NamedEntity {
|
||||
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();
|
||||
|
||||
|
||||
/**
|
||||
* @return number of {@link Target}s in this rollout.
|
||||
*/
|
||||
@@ -123,9 +129,22 @@ public interface Rollout extends NamedEntity {
|
||||
*/
|
||||
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
|
||||
* problem during asynchronous creating.
|
||||
*
|
||||
* @deprecated legacy status is not used anymore
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -134,6 +153,7 @@ public interface Rollout extends NamedEntity {
|
||||
/**
|
||||
* Rollout could not be started due to errors, might be database problem
|
||||
* during asynchronous starting.
|
||||
*
|
||||
* @deprecated legacy status is not used anymore
|
||||
*/
|
||||
@Deprecated
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.Optional;
|
||||
import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.DistributionSetTagDeletedEvent;
|
||||
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.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
|
||||
@@ -91,12 +92,13 @@ public class EventType {
|
||||
|
||||
// download
|
||||
TYPES.put(20, DownloadProgressEvent.class);
|
||||
|
||||
|
||||
TYPES.put(21, SoftwareModuleCreatedEvent.class);
|
||||
TYPES.put(22, SoftwareModuleDeletedEvent.class);
|
||||
TYPES.put(23, SoftwareModuleUpdatedEvent.class);
|
||||
|
||||
TYPES.put(24, TargetPollEvent.class);
|
||||
TYPES.put(25, RolloutDeletedEvent.class);
|
||||
}
|
||||
|
||||
private int value;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,34 +6,23 @@
|
||||
* which accompanies this distribution, and is available at
|
||||
* 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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.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.RolloutGroup;
|
||||
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() {
|
||||
}
|
||||
|
||||
@@ -43,7 +32,7 @@ final class RolloutHelper {
|
||||
* @param conditions
|
||||
* input conditions and actions
|
||||
*/
|
||||
static void verifyRolloutGroupConditions(final RolloutGroupConditions conditions) {
|
||||
public static void verifyRolloutGroupConditions(final RolloutGroupConditions conditions) {
|
||||
if (conditions.getSuccessCondition() == null) {
|
||||
throw new ConstraintViolationException("Rollout group is missing success condition");
|
||||
}
|
||||
@@ -60,7 +49,7 @@ final class RolloutHelper {
|
||||
* the input 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) {
|
||||
throw new ConstraintViolationException("Target percentage has to be between 1 and 100");
|
||||
}
|
||||
@@ -74,55 +63,13 @@ final class RolloutHelper {
|
||||
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
|
||||
*
|
||||
* @param amountGroup
|
||||
* amount of groups
|
||||
*/
|
||||
static void verifyRolloutGroupParameter(final int amountGroup) {
|
||||
public static void verifyRolloutGroupParameter(final int amountGroup) {
|
||||
if (amountGroup <= 0) {
|
||||
throw new ConstraintViolationException("the amountGroup must be greater than zero");
|
||||
} else if (amountGroup > 500) {
|
||||
@@ -136,7 +83,7 @@ final class RolloutHelper {
|
||||
* @param percentage
|
||||
* the percentage
|
||||
*/
|
||||
static void verifyRolloutGroupTargetPercentage(final float percentage) {
|
||||
public static void verifyRolloutGroupTargetPercentage(final float percentage) {
|
||||
if (percentage <= 0) {
|
||||
throw new ConstraintViolationException("the percentage must be greater than zero");
|
||||
} else if (percentage > 100) {
|
||||
@@ -152,7 +99,7 @@ final class RolloutHelper {
|
||||
* Rollout to derive the filter from
|
||||
* @return resulting target filter query
|
||||
*/
|
||||
static String getTargetFilterQuery(final Rollout rollout) {
|
||||
public static String getTargetFilterQuery(final Rollout rollout) {
|
||||
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
|
||||
* 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) {
|
||||
return targetFilter + ";createdat=le=" + createdAt.toString();
|
||||
}
|
||||
@@ -179,7 +126,7 @@ final class RolloutHelper {
|
||||
* @param 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)) {
|
||||
throw new RolloutIllegalStateException("Rollout is not in status " + status.toString());
|
||||
}
|
||||
@@ -197,7 +144,7 @@ final class RolloutHelper {
|
||||
* the group to add
|
||||
* @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) {
|
||||
return rollout.getRolloutGroups().stream()
|
||||
.filter(innerGroup -> innerGroup.getStatus().equals(status) || innerGroup.equals(group))
|
||||
@@ -211,7 +158,7 @@ final class RolloutHelper {
|
||||
* the rollout
|
||||
* @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) -> {
|
||||
if (group1.getId() < group2.getId()) {
|
||||
return -1;
|
||||
@@ -232,7 +179,7 @@ final class RolloutHelper {
|
||||
* @return RSQL string without base filter of the Rollout. Can be an empty
|
||||
* 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()))) {
|
||||
return "";
|
||||
}
|
||||
@@ -254,7 +201,7 @@ final class RolloutHelper {
|
||||
* @return RSQL string without base filter of the Rollout. Can be an empty
|
||||
* 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 String groupFilter = group.getTargetFilterQuery();
|
||||
// when any previous group has the same filter as the target group the
|
||||
@@ -283,7 +230,7 @@ final class RolloutHelper {
|
||||
&& prevGroup.getTargetFilterQuery().equals(groupFilter));
|
||||
}
|
||||
|
||||
private static String concatAndTargetFilters(String... filters) {
|
||||
private static String concatAndTargetFilters(final String... filters) {
|
||||
return "(" + Arrays.stream(filters).collect(Collectors.joining(");(")) + ")";
|
||||
}
|
||||
|
||||
@@ -308,7 +255,7 @@ final class RolloutHelper {
|
||||
* @param targetCount
|
||||
* the count of left targets
|
||||
*/
|
||||
static void verifyRemainingTargets(final long targetCount) {
|
||||
public static void verifyRemainingTargets(final long targetCount) {
|
||||
if (targetCount > 0) {
|
||||
throw new ConstraintViolationException(
|
||||
"Rollout groups don't match all targets that are targeted by the rollout");
|
||||
@@ -318,35 +265,11 @@ final class RolloutHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
public static void checkIfRolloutCanStarted(final Rollout rollout, final Rollout mergedRollout) {
|
||||
if (!(Rollout.RolloutStatus.READY.equals(mergedRollout.getStatus()))) {
|
||||
throw new RolloutIllegalStateException("Rollout can only be started in state ready but current state is "
|
||||
+ 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -195,6 +195,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
|
||||
* the status which the actions should not have
|
||||
* @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")
|
||||
List<JpaAction> findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
* rolloutgroup in a specific status
|
||||
*/
|
||||
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
|
||||
Page<Action> findByRolloutAndRolloutGroupParentAndStatus(Pageable pageable, JpaRollout rollout,
|
||||
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
|
||||
* rolloutgroup in a specific status
|
||||
*/
|
||||
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
|
||||
Page<Action> findByRolloutAndRolloutGroupParentIsNullAndStatus(Pageable pageable, JpaRollout rollout,
|
||||
Status actionStatus);
|
||||
|
||||
/**
|
||||
* Retrieves all actions for a specific rollout and in a specific status.
|
||||
*
|
||||
*
|
||||
* @param pageable
|
||||
* page parameters
|
||||
* @param rolloutId
|
||||
* the rollout the actions beglong to
|
||||
* @param actionStatus
|
||||
* the status of the actions
|
||||
* @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
|
||||
@@ -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")
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
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.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
@@ -72,11 +71,11 @@ public interface DistributionSetRepository
|
||||
* Finds {@link DistributionSet}s where given {@link SoftwareModule} is
|
||||
* assigned.
|
||||
*
|
||||
* @param module
|
||||
* @param moduleId
|
||||
* to search for
|
||||
* @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
|
||||
|
||||
@@ -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.DbArtifactHash;
|
||||
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.ArtifactUploadFailedException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
@@ -104,23 +103,16 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public boolean clearArtifactBinary(final Long artifactId) {
|
||||
return clearArtifactBinary(localArtifactRepository.findById(artifactId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, artifactId)));
|
||||
}
|
||||
public boolean clearArtifactBinary(final String sha1Hash, final Long moduleId) {
|
||||
|
||||
private boolean clearArtifactBinary(final JpaArtifact existing) {
|
||||
|
||||
for (final Artifact lArtifact : localArtifactRepository.findBySha1Hash(existing.getSha1Hash())) {
|
||||
if (!lArtifact.getSoftwareModule().isDeleted()
|
||||
&& Long.compare(lArtifact.getSoftwareModule().getId(), existing.getSoftwareModule().getId()) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (localArtifactRepository.existsWithSha1HashAndSoftwareModuleIdIsNot(sha1Hash, moduleId)) {
|
||||
// there are still other artifacts that need the binary
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
LOG.debug("deleting artifact from repository {}", existing.getSha1Hash());
|
||||
artifactRepository.deleteBySha1(existing.getSha1Hash());
|
||||
LOG.debug("deleting artifact from repository {}", sha1Hash);
|
||||
artifactRepository.deleteBySha1(sha1Hash);
|
||||
return true;
|
||||
} catch (final ArtifactStoreException e) {
|
||||
throw new ArtifactDeleteFailedException(e);
|
||||
@@ -134,7 +126,7 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
final JpaArtifact existing = (JpaArtifact) findArtifact(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, id));
|
||||
|
||||
clearArtifactBinary(existing);
|
||||
clearArtifactBinary(existing.getSha1Hash(), existing.getSoftwareModule().getId());
|
||||
|
||||
((JpaSoftwareModule) existing.getSoftwareModule()).removeArtifact(existing);
|
||||
softwareModuleRepository.save((JpaSoftwareModule) existing.getSoftwareModule());
|
||||
@@ -148,6 +140,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
|
||||
@Override
|
||||
public Optional<Artifact> findByFilenameAndSoftwareModule(final String filename, final Long softwareModuleId) {
|
||||
throwExceptionIfSoftwareModuleDoesNotExist(softwareModuleId);
|
||||
|
||||
return localArtifactRepository.findFirstByFilenameAndSoftwareModuleId(filename, softwareModuleId);
|
||||
}
|
||||
|
||||
@@ -163,13 +157,20 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
|
||||
@Override
|
||||
public Page<Artifact> findArtifactBySoftwareModule(final Pageable pageReq, final Long swId) {
|
||||
throwExceptionIfSoftwareModuleDoesNotExist(swId);
|
||||
|
||||
return localArtifactRepository.findBySoftwareModuleId(pageReq, swId);
|
||||
}
|
||||
|
||||
private void throwExceptionIfSoftwareModuleDoesNotExist(final Long swId) {
|
||||
if (!softwareModuleRepository.exists(swId)) {
|
||||
throw new EntityNotFoundException(SoftwareModule.class, swId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DbArtifact loadArtifactBinary(final String sha1Hash) {
|
||||
return Optional.ofNullable(artifactRepository.getArtifactBySha1(sha1Hash))
|
||||
.orElseThrow(() -> new ArtifactBinaryNotFoundException(sha1Hash));
|
||||
public Optional<DbArtifact> loadArtifactBinary(final String sha1Hash) {
|
||||
return Optional.ofNullable(artifactRepository.getArtifactBySha1(sha1Hash));
|
||||
}
|
||||
|
||||
private Artifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename,
|
||||
|
||||
@@ -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.Status;
|
||||
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.TargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
@@ -85,6 +86,9 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
@Autowired
|
||||
private TargetRepository targetRepository;
|
||||
|
||||
@Autowired
|
||||
private SoftwareModuleRepository softwareModuleRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetManagement targetManagement;
|
||||
|
||||
@@ -140,6 +144,9 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
@Override
|
||||
public Optional<Action> getActionForDownloadByTargetAndSoftwareModule(final String controllerId,
|
||||
final Long moduleId) {
|
||||
throwExceptionIfTargetDoesNotExist(controllerId);
|
||||
throwExceptionIfSoftwareModuleDoesNotExist(moduleId);
|
||||
|
||||
final List<Action> action = actionRepository.findActionByTargetAndSoftwareModule(controllerId, moduleId);
|
||||
|
||||
if (action.isEmpty() || action.get(0).isCancelingOrCanceled()) {
|
||||
@@ -149,26 +156,40 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
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
|
||||
public boolean hasTargetArtifactAssigned(final String controllerId, final String sha1Hash) {
|
||||
final Optional<Target> target = targetRepository.findByControllerId(controllerId);
|
||||
if (!target.isPresent()) {
|
||||
return false;
|
||||
}
|
||||
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target.get(), sha1Hash)) > 0;
|
||||
throwExceptionIfTargetDoesNotExist(controllerId);
|
||||
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(controllerId, sha1Hash)) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTargetArtifactAssigned(final Long targetId, final String sha1Hash) {
|
||||
final Target target = targetRepository.findOne(targetId);
|
||||
if (target == null) {
|
||||
return false;
|
||||
}
|
||||
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target, sha1Hash)) > 0;
|
||||
throwExceptionIfTargetDoesNotExist(targetId);
|
||||
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(targetId, sha1Hash)) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Action> findOldestActiveActionByTarget(final String controllerId) {
|
||||
throwExceptionIfTargetDoesNotExist(controllerId);
|
||||
|
||||
// used in favorite to findFirstByTargetAndActiveOrderByIdAsc due to
|
||||
// DATAJPA-841 issue.
|
||||
return actionRepository.findFirstByTargetControllerIdAndActive(new Sort(Direction.ASC, "id"), controllerId,
|
||||
|
||||
@@ -225,7 +225,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
final List<JpaTarget> targets = Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream()
|
||||
.map(ids -> targetRepository
|
||||
.findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, set.getId())))
|
||||
.flatMap(t -> t.stream()).collect(Collectors.toList());
|
||||
.flatMap(List::stream).collect(Collectors.toList());
|
||||
|
||||
if (targets.isEmpty()) {
|
||||
// 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 DefaultTransactionDefinition def = new DefaultTransactionDefinition();
|
||||
def.setName("startScheduledActions");
|
||||
def.setReadOnly(false);
|
||||
def.setIsolationLevel(Isolation.READ_UNCOMMITTED.value());
|
||||
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
return new TransactionTemplate(txManager, def).execute(status -> {
|
||||
final Page<Action> rolloutGroupActions = findActionsByRolloutAndRolloutGroupParent(rollout,
|
||||
@@ -599,24 +601,38 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
|
||||
@Override
|
||||
public List<Action> findActiveActionsByTarget(final String controllerId) {
|
||||
throwExceptionIfTargetFoesNotExist(controllerId);
|
||||
|
||||
return actionRepository.findByActiveAndTarget(controllerId, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> findInActiveActionsByTarget(final String controllerId) {
|
||||
throwExceptionIfTargetFoesNotExist(controllerId);
|
||||
|
||||
return actionRepository.findByActiveAndTarget(controllerId, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countActionsByTarget(final String controllerId) {
|
||||
throwExceptionIfTargetFoesNotExist(controllerId);
|
||||
|
||||
return actionRepository.countByTargetControllerId(controllerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countActionsByTarget(final String rsqlParam, final String controllerId) {
|
||||
throwExceptionIfTargetFoesNotExist(controllerId);
|
||||
|
||||
return actionRepository.count(createSpecificationFor(controllerId, rsqlParam));
|
||||
}
|
||||
|
||||
private void throwExceptionIfTargetFoesNotExist(final String controllerId) {
|
||||
if (!targetRepository.existsByControllerId(controllerId)) {
|
||||
throw new EntityNotFoundException(Target.class, controllerId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
|
||||
@@ -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.rsql.RSQLUtility;
|
||||
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.RolloutGroup;
|
||||
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.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
@@ -65,6 +65,9 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
@Autowired
|
||||
private RolloutGroupRepository rolloutGroupRepository;
|
||||
|
||||
@Autowired
|
||||
private RolloutRepository rolloutRepository;
|
||||
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
|
||||
@@ -115,9 +118,19 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
|
||||
@Override
|
||||
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 List<Long> rolloutGroupIds = rolloutGroups.getContent().stream().map(rollout -> rollout.getId())
|
||||
final List<Long> rolloutGroupIds = rolloutGroups.getContent().stream().map(RolloutGroup::getId)
|
||||
.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(
|
||||
rolloutGroupIds);
|
||||
|
||||
@@ -192,8 +205,9 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetWithActionStatus> findAllTargetsWithActionStatus(final PageRequest pageRequest,
|
||||
public Page<TargetWithActionStatus> findAllTargetsWithActionStatus(final Pageable pageRequest,
|
||||
final Long rolloutGroupId) {
|
||||
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
|
||||
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
|
||||
@@ -218,11 +232,14 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
.setFirstResult(pageRequest.getOffset()).setMaxResults(pageRequest.getPageSize()).getResultList()
|
||||
.stream().map(o -> new TargetWithActionStatus((Target) o[0], (Action.Status) o[1]))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countTargetsOfRolloutsGroup(@NotNull final Long rolloutGroupId) {
|
||||
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
|
||||
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
|
||||
final Root<RolloutTargetGroup> countQueryFrom = countQuery.from(RolloutTargetGroup.class);
|
||||
@@ -231,4 +248,10 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
return entityManager.createQuery(countQuery).getSingleResult();
|
||||
}
|
||||
|
||||
private void throwExceptionIfRolloutGroupDoesNotExist(final Long rolloutGroupId) {
|
||||
if (!rolloutGroupRepository.exists(rolloutGroupId)) {
|
||||
throw new EntityNotFoundException(RolloutGroup.class, rolloutGroupId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,22 +8,23 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import javax.validation.ConstraintDeclarationException;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.hawkbit.repository.AbstractRolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutFields;
|
||||
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutHelper;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
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.RolloutUpdate;
|
||||
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.EntityAlreadyExistsException;
|
||||
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.RolloutGroupConditionEvaluator;
|
||||
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.ActionType;
|
||||
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.TotalTargetCountActionStatus;
|
||||
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.tenancy.TenantAware;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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.jpa.domain.Specification;
|
||||
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.AsyncResult;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
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.validation.annotation.Validated;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link RolloutManagement}.
|
||||
*/
|
||||
@Validated
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public class JpaRolloutManagement implements RolloutManagement {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class);
|
||||
public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(JpaRolloutManagement.class);
|
||||
|
||||
/**
|
||||
* 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
|
||||
private RolloutRepository rolloutRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetManagement targetManagement;
|
||||
|
||||
@Autowired
|
||||
private RolloutGroupRepository rolloutGroupRepository;
|
||||
|
||||
@Autowired
|
||||
private DeploymentManagement deploymentManagement;
|
||||
|
||||
@Autowired
|
||||
private RolloutTargetGroupRepository rolloutTargetGroupRepository;
|
||||
|
||||
@Autowired
|
||||
private RolloutGroupManagement rolloutGroupManagement;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetManagement distributionSetManagement;
|
||||
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@Autowired
|
||||
private NoCountPagingRepository criteriaNoCountDao;
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager txManager;
|
||||
|
||||
@Autowired
|
||||
private VirtualPropertyReplacer virtualPropertyReplacer;
|
||||
|
||||
@Autowired
|
||||
private AfterTransactionCommitExecutor afterCommit;
|
||||
|
||||
@Override
|
||||
public Page<Rollout> findAll(final Pageable pageable) {
|
||||
return RolloutHelper.convertPage(rolloutRepository.findAll(pageable), pageable);
|
||||
JpaRolloutManagement(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) {
|
||||
super(targetManagement, deploymentManagement, rolloutGroupManagement, distributionSetManagement, context,
|
||||
eventPublisher, virtualPropertyReplacer, txManager, tenantAware, lockRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Rollout> findAllByPredicate(final String rsqlParam, final Pageable pageable) {
|
||||
final Specification<JpaRollout> specification = RSQLUtility.parse(rsqlParam, RolloutFields.class,
|
||||
virtualPropertyReplacer);
|
||||
public Page<Rollout> findAll(final Pageable pageable, final boolean deleted) {
|
||||
final Specification<JpaRollout> spec = RolloutSpecification.isDeleted(deleted);
|
||||
return JpaRolloutHelper.convertPage(rolloutRepository.findAll(spec, pageable), pageable);
|
||||
}
|
||||
|
||||
final Page<JpaRollout> findAll = rolloutRepository.findAll(specification, pageable);
|
||||
return RolloutHelper.convertPage(findAll, pageable);
|
||||
@Override
|
||||
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
|
||||
@@ -225,8 +231,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
group.setTargetPercentage(1.0F / (amountOfGroups - i) * 100);
|
||||
|
||||
lastSavedGroup = rolloutGroupRepository.save(group);
|
||||
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup);
|
||||
|
||||
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
|
||||
}
|
||||
|
||||
savedRollout.setRolloutGroupsCreated(amountOfGroups);
|
||||
@@ -240,7 +245,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
// Preparing the groups
|
||||
final List<RolloutGroup> groups = groupList.stream()
|
||||
.map(group -> RolloutHelper.prepareRolloutGroupWithDefaultConditions(group, conditions))
|
||||
.map(group -> JpaRolloutHelper.prepareRolloutGroupWithDefaultConditions(group, conditions))
|
||||
.collect(Collectors.toList());
|
||||
groups.forEach(RolloutHelper::verifyRolloutGroupHasConditions);
|
||||
|
||||
@@ -261,7 +266,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
if (srcGroup.getTargetFilterQuery() != null) {
|
||||
group.setTargetFilterQuery(srcGroup.getTargetFilterQuery());
|
||||
} else {
|
||||
group.setTargetFilterQuery("");
|
||||
group.setTargetFilterQuery(StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
group.setSuccessCondition(srcGroup.getSuccessCondition());
|
||||
@@ -277,25 +282,20 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
group.setErrorActionExp(srcGroup.getErrorActionExp());
|
||||
|
||||
lastSavedGroup = rolloutGroupRepository.save(group);
|
||||
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup);
|
||||
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
|
||||
}
|
||||
|
||||
savedRollout.setRolloutGroupsCreated(groups.size());
|
||||
return rolloutRepository.save(savedRollout);
|
||||
}
|
||||
|
||||
private void publishRolloutGroupCreatedEventAfterCommit(final RolloutGroup group) {
|
||||
afterCommit
|
||||
.afterCommit(() -> eventPublisher.publishEvent(new RolloutGroupCreatedEvent(group, context.getId())));
|
||||
private void publishRolloutGroupCreatedEventAfterCommit(final RolloutGroup group, final Rollout rollout) {
|
||||
afterCommit.afterCommit(() -> eventPublisher
|
||||
.publishEvent(new RolloutGroupCreatedEvent(group, rollout.getId(), context.getId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public void fillRolloutGroupsWithTargets(final Long rolloutId) {
|
||||
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
|
||||
|
||||
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.CREATING);
|
||||
private void handleCreateRollout(final JpaRollout rollout) {
|
||||
LOGGER.debug("handleCreateRollout called for rollout {}", rollout.getId());
|
||||
|
||||
final List<RolloutGroup> rolloutGroups = RolloutHelper.getOrderedGroups(rollout);
|
||||
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
|
||||
// ready, too.
|
||||
if (readyGroups == rolloutGroups.size()) {
|
||||
LOGGER.debug("rollout {} creatin done. Switch to READY.", rollout.getId());
|
||||
rollout.setStatus(RolloutStatus.READY);
|
||||
rollout.setLastCheck(0);
|
||||
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,
|
||||
final String targetFilter, final long limit) {
|
||||
|
||||
return runInNewCountingTransaction("assignTargetsToRolloutGroup", status -> {
|
||||
return runInNewTransaction("assignTargetsToRolloutGroup", status -> {
|
||||
final PageRequest pageRequest = new PageRequest(0, Math.toIntExact(limit));
|
||||
final List<Long> readyGroups = RolloutHelper.getGroupsByStatusIncludingGroup(rollout,
|
||||
RolloutGroupStatus.READY, group);
|
||||
@@ -399,19 +393,6 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
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
|
||||
@Async
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public Rollout startRollout(final Long rolloutId) {
|
||||
LOGGER.debug("startRollout called for rollout {}", rolloutId);
|
||||
|
||||
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
|
||||
RolloutHelper.checkIfRolloutCanStarted(rollout, rollout);
|
||||
rollout.setStatus(RolloutStatus.STARTING);
|
||||
@@ -496,6 +422,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
}
|
||||
|
||||
private void startFirstRolloutGroup(final Rollout rollout) {
|
||||
LOGGER.debug("startFirstRolloutGroup called for rollout {}", rollout.getId());
|
||||
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.STARTING);
|
||||
final JpaRollout jpaRollout = (JpaRollout) rollout;
|
||||
|
||||
@@ -516,7 +443,6 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
}
|
||||
|
||||
private boolean ensureAllGroupsAreScheduled(final Rollout rollout) {
|
||||
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.STARTING);
|
||||
final JpaRollout jpaRollout = (JpaRollout) 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) {
|
||||
return runInNewCountingTransaction("createActionsForTargets", status -> {
|
||||
return runInNewTransaction("createActionsForTargets", status -> {
|
||||
final PageRequest pageRequest = new PageRequest(0, limit);
|
||||
final Rollout rollout = rolloutRepository.findOne(rolloutId);
|
||||
final RolloutGroup group = rolloutGroupRepository.findOne(groupId);
|
||||
@@ -618,7 +544,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
@Modifying
|
||||
public void pauseRollout(final Long 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 "
|
||||
+ rollout.getStatus().name().toLowerCase());
|
||||
}
|
||||
@@ -644,39 +570,27 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public void checkRunningRollouts(final long delayBetweenChecks) {
|
||||
final List<JpaRollout> rolloutsToCheck = getRolloutsToCheckForStatus(delayBetweenChecks, RolloutStatus.RUNNING);
|
||||
if (rolloutsToCheck.isEmpty()) {
|
||||
return;
|
||||
private void handleRunningRollout(final JpaRollout rollout) {
|
||||
LOGGER.debug("handleRunningRollout called for rollout {}", rollout.getId());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
LOGGER.info("Found {} running rollouts to check", rolloutsToCheck.size());
|
||||
|
||||
for (final JpaRollout rollout : rolloutsToCheck) {
|
||||
LOGGER.debug("Checking rollout {}", 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);
|
||||
}
|
||||
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) {
|
||||
final Long groupsActiveLeft = rolloutGroupRepository.countByRolloutAndStatusOrStatus(rollout,
|
||||
final Long groupsActiveLeft = rolloutGroupRepository.countByRolloutIdAndStatusOrStatus(rollout.getId(),
|
||||
RolloutGroupStatus.RUNNING, RolloutGroupStatus.SCHEDULED);
|
||||
return groupsActiveLeft == 0;
|
||||
}
|
||||
@@ -798,90 +712,170 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public void checkCreatingRollouts(final long delayBetweenChecks) {
|
||||
final List<Long> rolloutsToCheck = getRolloutsToCheckForStatus(delayBetweenChecks, RolloutStatus.CREATING)
|
||||
.stream().map(Rollout::getId).collect(Collectors.toList());
|
||||
if (rolloutsToCheck.isEmpty()) {
|
||||
// No transaction, will be created per handled rollout
|
||||
@Transactional(propagation = Propagation.NEVER)
|
||||
public void handleRollouts() {
|
||||
rolloutRepository
|
||||
.findByStatusIn(Lists.newArrayList(RolloutStatus.CREATING, RolloutStatus.DELETING,
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public void checkStartingRollouts(final long delayBetweenChecks) {
|
||||
final List<JpaRollout> rolloutsToCheck = getRolloutsToCheckForStatus(delayBetweenChecks,
|
||||
RolloutStatus.STARTING);
|
||||
if (rolloutsToCheck.isEmpty()) {
|
||||
public void deleteRollout(final long rolloutId) {
|
||||
final JpaRollout jpaRollout = rolloutRepository.findOne(rolloutId);
|
||||
if (RolloutStatus.DELETING.equals(jpaRollout.getStatus())) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.info("Found {} starting rollouts to check", rolloutsToCheck.size());
|
||||
|
||||
rolloutsToCheck.forEach(rollout -> {
|
||||
if (ensureAllGroupsAreScheduled(rollout)) {
|
||||
startFirstRolloutGroup(rollout);
|
||||
}
|
||||
});
|
||||
jpaRollout.setStatus(RolloutStatus.DELETING);
|
||||
rolloutRepository.save(jpaRollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public void checkReadyRollouts(final long delayBetweenChecks) {
|
||||
final List<JpaRollout> rolloutsToCheck = getRolloutsToCheckForStatus(delayBetweenChecks, RolloutStatus.READY);
|
||||
if (rolloutsToCheck.isEmpty()) {
|
||||
private void handleDeleteRollout(final JpaRollout rollout) {
|
||||
LOGGER.debug("handleDeleteRollout called for {}", rollout.getId());
|
||||
|
||||
// check if there are actions beyond schedule
|
||||
boolean hardDeleteRolloutGroups = !actionRepository.existsByRolloutIdAndStatusNotIn(rollout.getId(),
|
||||
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;
|
||||
}
|
||||
|
||||
LOGGER.info("Found {} ready rollouts to check", rolloutsToCheck.size());
|
||||
|
||||
final long now = System.currentTimeMillis();
|
||||
|
||||
rolloutsToCheck.forEach(rollout -> {
|
||||
if (rollout.getStartAt() != null && rollout.getStartAt() <= now) {
|
||||
startRollout(rollout.getId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
// 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
|
||||
// running before, so only soft delete.
|
||||
hardDeleteRolloutGroups = !actionRepository.existsByRolloutId(rollout.getId());
|
||||
if (hardDeleteRolloutGroups) {
|
||||
hardDeleteRollout(rollout);
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
public Long countRolloutsAll() {
|
||||
return rolloutRepository.count();
|
||||
return rolloutRepository.count(RolloutSpecification.isDeleted(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countRolloutsAllByFilters(final String searchText) {
|
||||
return rolloutRepository.count(RolloutHelper.likeNameOrDescription(searchText));
|
||||
return rolloutRepository.count(JpaRolloutHelper.likeNameOrDescription(searchText, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Rollout> findRolloutWithDetailedStatusByFilters(final Pageable pageable, final String searchText) {
|
||||
final Specification<JpaRollout> specs = RolloutHelper.likeNameOrDescription(searchText);
|
||||
final Slice<JpaRollout> findAll = criteriaNoCountDao.findAll(specs, pageable, JpaRollout.class);
|
||||
public Slice<Rollout> findRolloutWithDetailedStatusByFilters(final Pageable pageable, final String searchText,
|
||||
final boolean deleted) {
|
||||
final Slice<JpaRollout> findAll = findByCriteriaAPI(pageable,
|
||||
Lists.newArrayList(JpaRolloutHelper.likeNameOrDescription(searchText, deleted)));
|
||||
setRolloutStatusDetails(findAll);
|
||||
return RolloutHelper.convertPage(findAll, pageable);
|
||||
return JpaRolloutHelper.convertPage(findAll, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -917,10 +911,12 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Rollout> findAllRolloutsWithDetailedStatus(final Pageable pageable) {
|
||||
final Page<JpaRollout> rollouts = rolloutRepository.findAll(pageable);
|
||||
public Page<Rollout> findAllRolloutsWithDetailedStatus(final Pageable pageable, final boolean deleted) {
|
||||
Page<JpaRollout> rollouts;
|
||||
final Specification<JpaRollout> spec = RolloutSpecification.isDeleted(deleted);
|
||||
rollouts = rolloutRepository.findAll(spec, pageable);
|
||||
setRolloutStatusDetails(rollouts);
|
||||
return RolloutHelper.convertPage(rollouts, pageable);
|
||||
return JpaRolloutHelper.convertPage(rollouts, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -940,8 +936,12 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
}
|
||||
|
||||
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRollout(final List<Long> rolloutIds) {
|
||||
final List<TotalTargetCountActionStatus> resultList = actionRepository.getStatusCountByRolloutId(rolloutIds);
|
||||
return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
|
||||
if (!rolloutIds.isEmpty()) {
|
||||
final List<TotalTargetCountActionStatus> resultList = actionRepository
|
||||
.getStatusCountByRolloutId(rolloutIds);
|
||||
return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setRolloutStatusDetails(final Slice<JpaRollout> rollouts) {
|
||||
@@ -949,10 +949,12 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRollout(
|
||||
rolloutIds);
|
||||
|
||||
for (final Rollout rollout : rollouts) {
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
||||
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets());
|
||||
((JpaRollout) rollout).setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
if (allStatesForRollout != null) {
|
||||
rollouts.forEach(rollout -> {
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
||||
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets());
|
||||
rollout.setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -209,8 +209,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
|
||||
return softwareModuleRepository.findOneByNameAndVersionAndTypeId(name, version, typeId);
|
||||
}
|
||||
|
||||
private boolean isUnassigned(final JpaSoftwareModule bsmMerged) {
|
||||
return distributionSetRepository.countByModules(bsmMerged) <= 0;
|
||||
private boolean isUnassigned(final Long moduleId) {
|
||||
return distributionSetRepository.countByModulesId(moduleId) <= 0;
|
||||
}
|
||||
|
||||
private Slice<JpaSoftwareModule> findSwModuleByCriteriaAPI(final Pageable pageable,
|
||||
@@ -225,7 +225,7 @@ public class JpaSoftwareManagement implements SoftwareManagement {
|
||||
|
||||
private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) {
|
||||
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
|
||||
deleteGridFsArtifacts(swModule);
|
||||
|
||||
if (isUnassigned(swModule)) {
|
||||
|
||||
softwareModuleRepository.delete(swModule);
|
||||
|
||||
if (isUnassigned(swModule.getId())) {
|
||||
softwareModuleRepository.delete(swModule.getId());
|
||||
} else {
|
||||
|
||||
assignedModuleIds.add(swModule.getId());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -218,8 +218,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant);
|
||||
targetRepository.deleteByTenantIgnoreCase(tenant);
|
||||
targetFilterQueryRepository.deleteByTenantIgnoreCase(tenant);
|
||||
actionRepository.deleteByTenantIgnoreCase(tenant);
|
||||
rolloutGroupRepository.deleteByTenantIgnoreCase(tenant);
|
||||
rolloutRepository.deleteByTenantIgnoreCase(tenant);
|
||||
artifactRepository.deleteByTenantIgnoreCase(tenant);
|
||||
targetTagRepository.deleteByTenantIgnoreCase(tenant);
|
||||
|
||||
@@ -607,7 +607,7 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
|
||||
final JpaTarget target = create.build();
|
||||
|
||||
if (targetRepository.findByControllerId(target.getControllerId()).isPresent()) {
|
||||
if (targetRepository.existsByControllerId(target.getControllerId())) {
|
||||
throw new EntityAlreadyExistsException();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,11 @@ import java.util.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
|
||||
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.Pageable;
|
||||
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.Transactional;
|
||||
|
||||
@@ -54,6 +56,21 @@ public interface LocalArtifactRepository extends BaseEntityRepository<JpaArtifac
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
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.RolloutGroupManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutProperties;
|
||||
import org.eclipse.hawkbit.repository.SoftwareManagement;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
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.SystemSecurityContextHolder;
|
||||
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.model.DistributionSet;
|
||||
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.springframework.beans.factory.ObjectProvider;
|
||||
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.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
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.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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.EnableJpaRepositories;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
|
||||
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
@@ -101,7 +107,7 @@ import com.google.common.collect.Maps;
|
||||
@EnableAspectJAutoProxy
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableConfigurationProperties({ RepositoryProperties.class, ControllerPollProperties.class, RolloutProperties.class,
|
||||
@EnableConfigurationProperties({ RepositoryProperties.class, ControllerPollProperties.class,
|
||||
TenantConfigurationProperties.class })
|
||||
@EnableScheduling
|
||||
@EntityScan("org.eclipse.hawkbit.repository.jpa.model")
|
||||
@@ -115,7 +121,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RsqlValidationOracle rsqlValidationOracle() {
|
||||
RsqlValidationOracle rsqlValidationOracle() {
|
||||
return new RsqlParserValidationOracle();
|
||||
}
|
||||
|
||||
@@ -127,7 +133,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* @return DistributionSetBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
public DistributionSetBuilder distributionSetBuilder(final DistributionSetManagement distributionSetManagement,
|
||||
DistributionSetBuilder distributionSetBuilder(final DistributionSetManagement distributionSetManagement,
|
||||
final SoftwareManagement softwareManagement) {
|
||||
return new JpaDistributionSetBuilder(distributionSetManagement, softwareManagement);
|
||||
}
|
||||
@@ -140,7 +146,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* @return DistributionSetTypeBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
public DistributionSetTypeBuilder distributionSetTypeBuilder(final SoftwareManagement softwareManagement) {
|
||||
DistributionSetTypeBuilder distributionSetTypeBuilder(final SoftwareManagement softwareManagement) {
|
||||
return new JpaDistributionSetTypeBuilder(softwareManagement);
|
||||
}
|
||||
|
||||
@@ -150,7 +156,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* @return SoftwareModuleBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
public SoftwareModuleBuilder softwareModuleBuilder(final SoftwareManagement softwareManagement) {
|
||||
SoftwareModuleBuilder softwareModuleBuilder(final SoftwareManagement softwareManagement) {
|
||||
return new JpaSoftwareModuleBuilder(softwareManagement);
|
||||
}
|
||||
|
||||
@@ -160,7 +166,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* @return RolloutBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
public RolloutBuilder rolloutBuilder(final DistributionSetManagement distributionSetManagement) {
|
||||
RolloutBuilder rolloutBuilder(final DistributionSetManagement distributionSetManagement) {
|
||||
return new JpaRolloutBuilder(distributionSetManagement);
|
||||
}
|
||||
|
||||
@@ -171,8 +177,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* @return TargetFilterQueryBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
public TargetFilterQueryBuilder targetFilterQueryBuilder(
|
||||
final DistributionSetManagement distributionSetManagement) {
|
||||
TargetFilterQueryBuilder targetFilterQueryBuilder(final DistributionSetManagement distributionSetManagement) {
|
||||
return new JpaTargetFilterQueryBuilder(distributionSetManagement);
|
||||
}
|
||||
|
||||
@@ -182,7 +187,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* e.g. JPA entities.
|
||||
*/
|
||||
@Bean
|
||||
public SystemSecurityContextHolder systemSecurityContextHolder() {
|
||||
SystemSecurityContextHolder systemSecurityContextHolder() {
|
||||
return SystemSecurityContextHolder.getInstance();
|
||||
}
|
||||
|
||||
@@ -192,7 +197,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* directly, e.g. JPA entities.
|
||||
*/
|
||||
@Bean
|
||||
public TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
|
||||
TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
|
||||
return TenantConfigurationManagementHolder.getInstance();
|
||||
}
|
||||
|
||||
@@ -203,7 +208,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* entities.
|
||||
*/
|
||||
@Bean
|
||||
public SystemManagementHolder systemManagementHolder() {
|
||||
SystemManagementHolder systemManagementHolder() {
|
||||
return SystemManagementHolder.getInstance();
|
||||
}
|
||||
|
||||
@@ -214,7 +219,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* entities.
|
||||
*/
|
||||
@Bean
|
||||
public TenantAwareHolder tenantAwareHolder() {
|
||||
TenantAwareHolder tenantAwareHolder() {
|
||||
return TenantAwareHolder.getInstance();
|
||||
}
|
||||
|
||||
@@ -225,7 +230,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* injection
|
||||
*/
|
||||
@Bean
|
||||
public SecurityTokenGeneratorHolder securityTokenGeneratorHolder() {
|
||||
SecurityTokenGeneratorHolder securityTokenGeneratorHolder() {
|
||||
return SecurityTokenGeneratorHolder.getInstance();
|
||||
}
|
||||
|
||||
@@ -233,7 +238,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* @return the singleton instance of the {@link EntityInterceptorHolder}
|
||||
*/
|
||||
@Bean
|
||||
public EntityInterceptorHolder entityInterceptorHolder() {
|
||||
EntityInterceptorHolder entityInterceptorHolder() {
|
||||
return EntityInterceptorHolder.getInstance();
|
||||
}
|
||||
|
||||
@@ -243,7 +248,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* {@link AfterTransactionCommitExecutorHolder}
|
||||
*/
|
||||
@Bean
|
||||
public AfterTransactionCommitExecutorHolder afterTransactionCommitExecutorHolder() {
|
||||
AfterTransactionCommitExecutorHolder afterTransactionCommitExecutorHolder() {
|
||||
return AfterTransactionCommitExecutorHolder.getInstance();
|
||||
}
|
||||
|
||||
@@ -261,7 +266,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* @return {@link ExceptionMappingAspectHandler} aspect bean
|
||||
*/
|
||||
@Bean
|
||||
public ExceptionMappingAspectHandler createRepositoryExceptionHandlerAdvice() {
|
||||
ExceptionMappingAspectHandler createRepositoryExceptionHandlerAdvice() {
|
||||
return new ExceptionMappingAspectHandler();
|
||||
}
|
||||
|
||||
@@ -305,7 +310,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SystemManagement systemManagement() {
|
||||
SystemManagement systemManagement() {
|
||||
return new JpaSystemManagement();
|
||||
}
|
||||
|
||||
@@ -316,7 +321,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ReportManagement reportManagement() {
|
||||
ReportManagement reportManagement() {
|
||||
return new JpaReportManagement();
|
||||
}
|
||||
|
||||
@@ -327,7 +332,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DistributionSetManagement distributionSetManagement() {
|
||||
DistributionSetManagement distributionSetManagement() {
|
||||
return new JpaDistributionSetManagement();
|
||||
}
|
||||
|
||||
@@ -338,7 +343,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TenantStatsManagement tenantStatsManagement() {
|
||||
TenantStatsManagement tenantStatsManagement() {
|
||||
return new JpaTenantStatsManagement();
|
||||
}
|
||||
|
||||
@@ -349,7 +354,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TenantConfigurationManagement tenantConfigurationManagement() {
|
||||
TenantConfigurationManagement tenantConfigurationManagement() {
|
||||
return new JpaTenantConfigurationManagement();
|
||||
}
|
||||
|
||||
@@ -360,7 +365,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TargetManagement targetManagement() {
|
||||
TargetManagement targetManagement() {
|
||||
return new JpaTargetManagement();
|
||||
}
|
||||
|
||||
@@ -378,7 +383,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TargetFilterQueryManagement targetFilterQueryManagement(
|
||||
TargetFilterQueryManagement targetFilterQueryManagement(
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||
final DistributionSetManagement distributionSetManagement) {
|
||||
@@ -393,7 +398,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TagManagement tagManagement() {
|
||||
TagManagement tagManagement() {
|
||||
return new JpaTagManagement();
|
||||
}
|
||||
|
||||
@@ -404,19 +409,21 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SoftwareManagement softwareManagement() {
|
||||
SoftwareManagement softwareManagement() {
|
||||
return new JpaSoftwareManagement();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link JpaRolloutManagement} bean.
|
||||
*
|
||||
* @return a new {@link RolloutManagement}
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RolloutManagement rolloutManagement() {
|
||||
return new JpaRolloutManagement();
|
||||
RolloutManagement rolloutManagement(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) {
|
||||
return new JpaRolloutManagement(targetManagement, deploymentManagement, rolloutGroupManagement,
|
||||
distributionSetManagement, context, eventPublisher, virtualPropertyReplacer, txManager, tenantAware,
|
||||
lockRegistry);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,7 +433,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RolloutGroupManagement rolloutGroupManagement() {
|
||||
RolloutGroupManagement rolloutGroupManagement() {
|
||||
return new JpaRolloutGroupManagement();
|
||||
}
|
||||
|
||||
@@ -437,7 +444,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DeploymentManagement deploymentManagement() {
|
||||
DeploymentManagement deploymentManagement() {
|
||||
return new JpaDeploymentManagement();
|
||||
}
|
||||
|
||||
@@ -448,7 +455,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ControllerManagement controllerManagement() {
|
||||
ControllerManagement controllerManagement() {
|
||||
return new JpaControllerManagement();
|
||||
}
|
||||
|
||||
@@ -460,7 +467,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ArtifactManagement artifactManagement() {
|
||||
ArtifactManagement artifactManagement() {
|
||||
return new JpaArtifactManagement();
|
||||
}
|
||||
|
||||
@@ -482,7 +489,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EventEntityManagerHolder eventEntityManagerHolder() {
|
||||
EventEntityManagerHolder eventEntityManagerHolder() {
|
||||
return EventEntityManagerHolder.getInstance();
|
||||
}
|
||||
|
||||
@@ -497,7 +504,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EventEntityManager eventEntityManager(final TenantAware aware, final EntityManager entityManager) {
|
||||
EventEntityManager eventEntityManager(final TenantAware aware, final EntityManager entityManager) {
|
||||
return new JpaEventEntityManager(aware, entityManager);
|
||||
}
|
||||
|
||||
@@ -516,7 +523,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public AutoAssignChecker autoAssignChecker(final TargetFilterQueryManagement targetFilterQueryManagement,
|
||||
AutoAssignChecker autoAssignChecker(final TargetFilterQueryManagement targetFilterQueryManagement,
|
||||
final TargetManagement targetManagement, final DeploymentManagement deploymentManagement,
|
||||
final PlatformTransactionManager transactionManager) {
|
||||
return new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement,
|
||||
@@ -525,6 +532,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
/**
|
||||
* {@link AutoAssignScheduler} bean.
|
||||
*
|
||||
* Note: does not activate in test profile, otherwise it is hard to test the
|
||||
* auto assign functionality.
|
||||
*
|
||||
* @param tenantAware
|
||||
* to run as specific tenant
|
||||
@@ -534,14 +544,49 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
* to run as system
|
||||
* @param autoAssignChecker
|
||||
* to run a check as tenant
|
||||
* @param lockRegistry
|
||||
* to lock the tenant for auto assigment
|
||||
* @return a new {@link AutoAssignChecker}
|
||||
*/
|
||||
@Bean
|
||||
@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)
|
||||
public AutoAssignScheduler autoAssignScheduler(final TenantAware tenantAware,
|
||||
final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext,
|
||||
final AutoAssignChecker autoAssignChecker) {
|
||||
return new AutoAssignScheduler(tenantAware, systemManagement, systemSecurityContext, autoAssignChecker);
|
||||
AutoAssignScheduler autoAssignScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
|
||||
final SystemSecurityContext systemSecurityContext, final AutoAssignChecker autoAssignChecker,
|
||||
final LockRegistry lockRegistry) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,8 +60,8 @@ public interface RolloutGroupRepository
|
||||
* rollout-management to find out rolloutgroups which are in specific
|
||||
* states.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroup belong to
|
||||
* @param rolloutId
|
||||
* the ID of the rollout the rolloutgroup belong to
|
||||
* @param rolloutGroupStatus1
|
||||
* the status of the rollout groups
|
||||
* @param rolloutGroupStatus2
|
||||
@@ -69,22 +69,45 @@ public interface RolloutGroupRepository
|
||||
* @return the count of rollout groups belonging to a rollout in specific
|
||||
* status
|
||||
*/
|
||||
@Query("SELECT COUNT(r.id) FROM JpaRolloutGroup r WHERE r.rollout = :rollout and (r.status = :status1 or r.status = :status2)")
|
||||
Long countByRolloutAndStatusOrStatus(@Param("rollout") JpaRollout rollout,
|
||||
@Query("SELECT COUNT(r.id) FROM JpaRolloutGroup r WHERE r.rollout.id = :rolloutId and (r.status = :status1 or r.status = :status2)")
|
||||
Long countByRolloutIdAndStatusOrStatus(@Param("rolloutId") long rolloutId,
|
||||
@Param("status1") RolloutGroupStatus rolloutGroupStatus1,
|
||||
@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
|
||||
* status. Retrieves the child rolloutgroup for a specific status.
|
||||
*
|
||||
* @param rolloutGroup
|
||||
* the parent rolloutgroup
|
||||
* @param rolloutGroupId
|
||||
* the rolloutgroupId to find the parents
|
||||
* @param status
|
||||
* the status of the rolloutgroups
|
||||
* @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
|
||||
@@ -124,4 +147,8 @@ public interface RolloutGroupRepository
|
||||
*/
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
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.RolloutStatus;
|
||||
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.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
@@ -30,36 +28,15 @@ public interface RolloutRepository
|
||||
extends BaseEntityRepository<JpaRollout, Long>, JpaSpecificationExecutor<JpaRollout> {
|
||||
|
||||
/**
|
||||
* Updates the {@code lastCheck} field of the {@link Rollout} for rollouts
|
||||
* in a specific status and only if the {@code lastCheck} is overdue.
|
||||
* Retrieves all {@link Rollout} for given status.
|
||||
*
|
||||
* @param lastCheck
|
||||
* the time in milliseconds to set to the lastCheck column
|
||||
* @param delay
|
||||
* the delay between last checks
|
||||
* @param status
|
||||
* the status which the rollout should have to update the last
|
||||
* check field
|
||||
* @return the count of the updated rows. Zero if no row has been updated
|
||||
* the status of the rollouts to find
|
||||
* @return the list of {@link Rollout} for specific status
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
|
||||
@Query("UPDATE JpaRollout r SET r.lastCheck = :lastCheck WHERE r.lastCheck < (:lastCheck - :delay) AND r.status=: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);
|
||||
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
|
||||
@Query("SELECT sm.id FROM JpaRollout sm WHERE sm.status IN ?1")
|
||||
List<Long> findByStatusIn(Collection<RolloutStatus> status);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Rollout} for a specific {@code name}
|
||||
|
||||
@@ -47,6 +47,15 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
|
||||
@EntityGraph(value = "Target.detail", type = EntityGraphType.LOAD)
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -97,6 +97,7 @@ public class AutoAssignChecker {
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void check() {
|
||||
LOGGER.debug("Auto assigned check call");
|
||||
|
||||
final PageRequest pageRequest = new PageRequest(0, PAGE_SIZE);
|
||||
|
||||
|
||||
@@ -9,27 +9,24 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.autoassign;
|
||||
|
||||
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.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
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 SystemManagement systemManagement;
|
||||
@@ -38,6 +35,8 @@ public class AutoAssignScheduler {
|
||||
|
||||
private final AutoAssignChecker autoAssignChecker;
|
||||
|
||||
private final LockRegistry lockRegistry;
|
||||
|
||||
/**
|
||||
* Instantiates a new AutoAssignScheduler
|
||||
*
|
||||
@@ -49,13 +48,17 @@ public class AutoAssignScheduler {
|
||||
* to run as system
|
||||
* @param autoAssignChecker
|
||||
* to run a check as tenant
|
||||
* @param lockRegistry
|
||||
* to acquire a lock per tenant
|
||||
*/
|
||||
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.systemManagement = systemManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.autoAssignChecker = autoAssignChecker;
|
||||
this.lockRegistry = lockRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,29 +67,38 @@ public class AutoAssignScheduler {
|
||||
* tenant the auto assignments defined in the target filter queries
|
||||
* {@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() {
|
||||
LOGGER.debug("auto assign 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 separately.
|
||||
final List<String> tenants = systemManagement.findTenants();
|
||||
LOGGER.info("Checking target filter queries for tenants: {}", tenants.size());
|
||||
for (final String tenant : tenants) {
|
||||
systemSecurityContext.runAsSystem(() -> executeAutoAssign());
|
||||
}
|
||||
|
||||
private Object executeAutoAssign() {
|
||||
// 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 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, () -> {
|
||||
|
||||
autoAssignChecker.check();
|
||||
|
||||
return null;
|
||||
});
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,12 +43,12 @@ public class DistributionSetTypeElement implements Serializable {
|
||||
|
||||
@MapsId("dsType")
|
||||
@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;
|
||||
|
||||
@MapsId("smType")
|
||||
@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;
|
||||
|
||||
public DistributionSetTypeElement() {
|
||||
|
||||
@@ -20,10 +20,10 @@ import javax.persistence.Embeddable;
|
||||
public class DistributionSetTypeElementCompositeKey implements Serializable {
|
||||
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;
|
||||
|
||||
@Column(name = "software_module_type", nullable = false)
|
||||
@Column(name = "software_module_type", nullable = false, updatable = false)
|
||||
private Long smType;
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa.model;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
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.model.Action;
|
||||
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.Rollout;
|
||||
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 {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "distribution_set", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_ds"))
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "distribution_set", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_ds"))
|
||||
@NotNull
|
||||
private JpaDistributionSet distributionSet;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "target", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_target"))
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "target", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_target"))
|
||||
@NotNull
|
||||
private JpaTarget target;
|
||||
|
||||
@@ -79,20 +79,20 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
@Column(name = "forced_time")
|
||||
private long forcedTime;
|
||||
|
||||
@Column(name = "status")
|
||||
@Column(name = "status", nullable = false)
|
||||
@NotNull
|
||||
private Status status;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(mappedBy = "action", targetEntity = JpaActionStatus.class, fetch = FetchType.LAZY, cascade = {
|
||||
CascadeType.REMOVE })
|
||||
private List<ActionStatus> actionStatus;
|
||||
@OneToMany(mappedBy = "action", targetEntity = JpaActionStatus.class, fetch = FetchType.LAZY)
|
||||
private List<JpaActionStatus> actionStatus;
|
||||
|
||||
@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;
|
||||
|
||||
@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;
|
||||
|
||||
@Override
|
||||
@@ -185,13 +185,15 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
@Override
|
||||
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new ActionCreatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
|
||||
.publishEvent(new ActionCreatedEvent(this, BaseEntity.getIdOrNull(rollout),
|
||||
BaseEntity.getIdOrNull(rolloutGroup), EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new ActionUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
|
||||
.publishEvent(new ActionUpdatedEvent(this, BaseEntity.getIdOrNull(rollout),
|
||||
BaseEntity.getIdOrNull(rolloutGroup), EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -54,11 +54,11 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
||||
private Long occurredAt;
|
||||
|
||||
@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
|
||||
private JpaAction action;
|
||||
|
||||
@Column(name = "status")
|
||||
@Column(name = "status", nullable = false, updatable = false)
|
||||
@NotNull
|
||||
private Status status;
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
@@ -49,7 +50,7 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art
|
||||
@NotEmpty
|
||||
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"))
|
||||
private JpaSoftwareModule softwareModule;
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
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.DistributionSetType;
|
||||
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.TargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
@@ -82,40 +79,38 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
||||
@CascadeOnDelete
|
||||
@ManyToMany(targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "sp_ds_module", joinColumns = {
|
||||
@JoinColumn(name = "ds_id", 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 = "ds_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_ds")) }, inverseJoinColumns = {
|
||||
@JoinColumn(name = "module_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_module")) })
|
||||
private Set<SoftwareModule> modules;
|
||||
|
||||
@CascadeOnDelete
|
||||
@ManyToMany(targetEntity = JpaDistributionSetTag.class)
|
||||
@JoinTable(name = "sp_ds_dstag", joinColumns = {
|
||||
@JoinColumn(name = "ds", 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 = "ds", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_ds")) }, inverseJoinColumns = {
|
||||
@JoinColumn(name = "TAG", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_tag")) })
|
||||
private Set<DistributionSetTag> tags;
|
||||
|
||||
@Column(name = "deleted")
|
||||
private boolean deleted;
|
||||
|
||||
@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)
|
||||
private List<TargetFilterQuery> autoAssignFilters;
|
||||
|
||||
@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)
|
||||
private List<Action> actions;
|
||||
private List<JpaAction> actions;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(fetch = FetchType.LAZY, targetEntity = JpaDistributionSetMetadata.class, cascade = {
|
||||
CascadeType.REMOVE })
|
||||
@JoinColumn(name = "ds_id", insertable = false, updatable = false)
|
||||
@OneToMany(mappedBy = "distributionSet", fetch = FetchType.LAZY, targetEntity = JpaDistributionSetMetadata.class)
|
||||
private List<DistributionSetMetadata> metadata;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, targetEntity = JpaDistributionSetType.class)
|
||||
@JoinColumn(name = "ds_id", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstype_ds"))
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false, targetEntity = JpaDistributionSetType.class)
|
||||
@JoinColumn(name = "ds_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstype_ds"))
|
||||
@NotNull
|
||||
private DistributionSetType type;
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ public class JpaDistributionSetMetadata extends JpaMetaData implements Distribut
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "ds_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_ds"))
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "ds_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_ds"))
|
||||
private JpaDistributionSet distributionSet;
|
||||
|
||||
public JpaDistributionSetMetadata() {
|
||||
|
||||
@@ -19,7 +19,6 @@ import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
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.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -49,12 +49,12 @@ import org.springframework.util.CollectionUtils;
|
||||
public class JpaDistributionSetType extends AbstractJpaNamedEntity implements DistributionSetType {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@OneToMany(targetEntity = DistributionSetTypeElement.class, cascade = {
|
||||
CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true)
|
||||
@JoinColumn(name = "distribution_set_type", insertable = false, updatable = false)
|
||||
@CascadeOnDelete
|
||||
@OneToMany(mappedBy = "dsType", targetEntity = DistributionSetTypeElement.class, cascade = {
|
||||
CascadeType.PERSIST }, fetch = FetchType.EAGER, orphanRemoval = true)
|
||||
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)
|
||||
@NotEmpty
|
||||
private String key;
|
||||
|
||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
@@ -28,6 +29,7 @@ import javax.persistence.UniqueConstraint;
|
||||
import javax.validation.constraints.NotNull;
|
||||
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.model.Action.ActionType;
|
||||
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.TotalTargetCountStatus;
|
||||
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.queries.UpdateObjectQuery;
|
||||
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
|
||||
import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
|
||||
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
|
||||
// sub entities
|
||||
@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 {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@OneToMany(targetEntity = JpaRolloutGroup.class)
|
||||
@JoinColumn(name = "rollout", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollout_rolloutgroup"))
|
||||
private List<RolloutGroup> rolloutGroups;
|
||||
private static final String DELETED_PROPERTY = "deleted";
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(targetEntity = JpaRolloutGroup.class, fetch = FetchType.LAZY, mappedBy = "rollout")
|
||||
private List<JpaRolloutGroup> rolloutGroups;
|
||||
|
||||
@Column(name = "target_filter", length = 1024, nullable = false)
|
||||
@Size(max = 1024)
|
||||
@NotEmpty
|
||||
private String targetFilterQuery;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "distribution_set", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolltout_ds"))
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "distribution_set", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolltout_ds"))
|
||||
@NotNull
|
||||
private JpaDistributionSet distributionSet;
|
||||
|
||||
@Column(name = "status")
|
||||
@Column(name = "status", nullable = false)
|
||||
@Convert("rolloutstatus")
|
||||
@NotNull
|
||||
private RolloutStatus status = RolloutStatus.CREATING;
|
||||
|
||||
@Column(name = "last_check")
|
||||
@@ -87,6 +112,9 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
||||
@Column(name = "rollout_groups_created")
|
||||
private int rolloutGroupsCreated;
|
||||
|
||||
@Column(name = "deleted")
|
||||
private boolean deleted;
|
||||
|
||||
@Column(name = "start_at")
|
||||
private Long startAt;
|
||||
|
||||
@@ -142,7 +170,7 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
||||
return startAt;
|
||||
}
|
||||
|
||||
public void setStartAt(Long startAt) {
|
||||
public void setStartAt(final Long startAt) {
|
||||
this.startAt = startAt;
|
||||
}
|
||||
|
||||
@@ -196,9 +224,8 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Rollout [rolloutGroups=" + rolloutGroups + ", targetFilterQuery=" + targetFilterQuery
|
||||
+ ", distributionSet=" + distributionSet + ", status=" + status + ", lastCheck=" + lastCheck
|
||||
+ ", getName()=" + getName() + ", getId()=" + getId() + "]";
|
||||
return "Rollout [ targetFilterQuery=" + targetFilterQuery + ", distributionSet=" + distributionSet + ", status="
|
||||
+ status + ", lastCheck=" + lastCheck + ", getName()=" + getName() + ", getId()=" + getId() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -211,11 +238,35 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.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
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
||||
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;
|
||||
@@ -60,18 +61,20 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@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;
|
||||
|
||||
@Column(name = "status")
|
||||
@Column(name = "status", nullable = false)
|
||||
@Convert("rolloutgroupstatus")
|
||||
private RolloutGroupStatus status = RolloutGroupStatus.CREATING;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST }, targetEntity = RolloutTargetGroup.class)
|
||||
@JoinColumn(name = "rolloutGroup_Id", insertable = false, updatable = false)
|
||||
@CascadeOnDelete
|
||||
@OneToMany(mappedBy = "rolloutGroup", fetch = FetchType.LAZY, cascade = {
|
||||
CascadeType.PERSIST }, targetEntity = RolloutTargetGroup.class)
|
||||
private List<RolloutTargetGroup> rolloutTargetGroup;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "parent_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolloutgroup_rolloutgroup"))
|
||||
private JpaRolloutGroup parent;
|
||||
|
||||
@Column(name = "success_condition", nullable = false)
|
||||
@@ -287,8 +290,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
|
||||
@Override
|
||||
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutGroupUpdatedEvent(this,
|
||||
|
||||
EventPublisherHolder.getInstance().getApplicationId()));
|
||||
this.getRollout().getId(), EventPublisherHolder.getInstance().getApplicationId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
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.DistributionSet;
|
||||
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.helper.EventPublisherHolder;
|
||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||
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
|
||||
@@ -60,8 +63,10 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implements SoftwareModule, EventAwareEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String DELETED_PROPERTY = "deleted";
|
||||
|
||||
@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
|
||||
private JpaSoftwareModuleType type;
|
||||
|
||||
@@ -77,13 +82,12 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "softwareModule", cascade = {
|
||||
CascadeType.ALL }, targetEntity = JpaArtifact.class)
|
||||
private List<Artifact> artifacts;
|
||||
CascadeType.PERSIST }, targetEntity = JpaArtifact.class, orphanRemoval = true)
|
||||
private List<JpaArtifact> artifacts;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaSoftwareModuleMetadata.class)
|
||||
@JoinColumn(name = "sw_id", insertable = false, updatable = false)
|
||||
private List<SoftwareModuleMetadata> metadata;
|
||||
@OneToMany(mappedBy = "softwareModule", fetch = FetchType.LAZY, targetEntity = JpaSoftwareModuleMetadata.class)
|
||||
private List<JpaSoftwareModuleMetadata> metadata;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
@@ -116,12 +120,12 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
||||
public void addArtifact(final Artifact artifact) {
|
||||
if (null == artifacts) {
|
||||
artifacts = new ArrayList<>(4);
|
||||
artifacts.add(artifact);
|
||||
artifacts.add((JpaArtifact) artifact);
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
|
||||
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
|
||||
|
||||
@@ -32,8 +32,8 @@ public class JpaSoftwareModuleMetadata extends JpaMetaData implements SoftwareMo
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@ManyToOne(targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "sw_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_sw"))
|
||||
@ManyToOne(optional = false, targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "sw_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_sw"))
|
||||
private SoftwareModule softwareModule;
|
||||
|
||||
public JpaSoftwareModuleMetadata() {
|
||||
|
||||
@@ -87,22 +87,21 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
|
||||
@CascadeOnDelete
|
||||
@ManyToMany(targetEntity = JpaTargetTag.class)
|
||||
@JoinTable(name = "sp_target_target_tag", joinColumns = {
|
||||
@JoinColumn(name = "target", 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 = "target", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_target")) }, inverseJoinColumns = {
|
||||
@JoinColumn(name = "tag", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_tag")) })
|
||||
private Set<TargetTag> tags;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, cascade = {
|
||||
CascadeType.REMOVE }, targetEntity = JpaAction.class)
|
||||
@JoinColumn(name = "target", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_act_hist_targ"))
|
||||
private List<Action> actions;
|
||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, targetEntity = JpaAction.class)
|
||||
private List<JpaAction> actions;
|
||||
|
||||
@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"))
|
||||
private JpaDistributionSet assignedDistributionSet;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToOne(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY, targetEntity = JpaTargetInfo.class)
|
||||
@OneToOne(cascade = { CascadeType.PERSIST,
|
||||
CascadeType.MERGE }, fetch = FetchType.LAZY, targetEntity = JpaTargetInfo.class)
|
||||
@PrimaryKeyJoinColumn
|
||||
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
|
||||
* with this security token.
|
||||
*/
|
||||
@Column(name = "sec_token", insertable = true, updatable = true, nullable = false, length = 128)
|
||||
@Size(max = 64)
|
||||
@Column(name = "sec_token", updatable = true, nullable = false, length = 128)
|
||||
@Size(max = 128)
|
||||
@NotEmpty
|
||||
private String securityToken;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE, CascadeType.PERSIST })
|
||||
@JoinColumn(name = "target_Id", insertable = false, updatable = false)
|
||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
|
||||
private List<RolloutTargetGroup> rolloutTargetGroup;
|
||||
|
||||
/**
|
||||
@@ -214,7 +212,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
|
||||
actions = new ArrayList<>();
|
||||
}
|
||||
|
||||
return actions.add(action);
|
||||
return actions.add((JpaAction) action);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -79,8 +79,7 @@ public class JpaTargetInfo implements Persistable<Long>, TargetInfo {
|
||||
private boolean entityNew;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToOne(cascade = { CascadeType.MERGE,
|
||||
CascadeType.REMOVE }, fetch = FetchType.LAZY, targetEntity = JpaTarget.class)
|
||||
@OneToOne(cascade = { CascadeType.MERGE }, fetch = FetchType.LAZY, targetEntity = JpaTarget.class)
|
||||
@JoinColumn(name = "target_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_stat_targ"))
|
||||
@MapsId
|
||||
private JpaTarget target;
|
||||
@@ -112,7 +111,7 @@ public class JpaTargetInfo implements Persistable<Long>, TargetInfo {
|
||||
@Column(name = "attribute_value", length = 128)
|
||||
@MapKeyColumn(name = "attribute_key", nullable = false, length = 32)
|
||||
@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>());
|
||||
|
||||
// set default request controller attributes to true, because we want to
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.hibernate.validator.constraints.NotEmpty;
|
||||
public class JpaTenantConfiguration extends AbstractJpaTenantAwareBaseEntity implements TenantConfiguration {
|
||||
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)
|
||||
@NotEmpty
|
||||
private String key;
|
||||
|
||||
@@ -43,7 +43,7 @@ import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||
public class JpaTenantMetaData extends AbstractJpaBaseEntity implements TenantMetaData {
|
||||
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)
|
||||
private String tenant;
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.persistence.annotations.ExistenceChecking;
|
||||
@@ -44,19 +43,22 @@ public class RolloutTargetGroup implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@ManyToOne(targetEntity = JpaRolloutGroup.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
|
||||
@JoinColumn(name = "rolloutGroup_Id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_group"))
|
||||
private RolloutGroup rolloutGroup;
|
||||
@ManyToOne(optional = false, targetEntity = JpaRolloutGroup.class, fetch = FetchType.LAZY, cascade = {
|
||||
CascadeType.PERSIST })
|
||||
@JoinColumn(name = "rolloutGroup_Id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_group"))
|
||||
private JpaRolloutGroup rolloutGroup;
|
||||
|
||||
@Id
|
||||
@ManyToOne(targetEntity = JpaTarget.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
|
||||
@JoinColumn(name = "target_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_target"))
|
||||
@ManyToOne(optional = false, targetEntity = JpaTarget.class, fetch = FetchType.LAZY, cascade = {
|
||||
CascadeType.PERSIST })
|
||||
@JoinColumn(name = "target_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_target"))
|
||||
private JpaTarget target;
|
||||
|
||||
@OneToMany(targetEntity = JpaAction.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
|
||||
@JoinColumns(value = { @JoinColumn(name = "rolloutgroup", referencedColumnName = "rolloutGroup_Id"),
|
||||
@JoinColumn(name = "target", referencedColumnName = "target_id") })
|
||||
private List<Action> actions;
|
||||
@JoinColumns(value = {
|
||||
@JoinColumn(name = "rolloutgroup", nullable = false, updatable = false, referencedColumnName = "rolloutGroup_Id"),
|
||||
@JoinColumn(name = "target", nullable = false, updatable = false, referencedColumnName = "target_id") })
|
||||
private List<JpaAction> actions;
|
||||
|
||||
/**
|
||||
* default constructor for JPA.
|
||||
@@ -66,7 +68,7 @@ public class RolloutTargetGroup implements Serializable {
|
||||
}
|
||||
|
||||
public RolloutTargetGroup(final RolloutGroup rolloutGroup, final Target target) {
|
||||
this.rolloutGroup = rolloutGroup;
|
||||
this.rolloutGroup = (JpaRolloutGroup) rolloutGroup;
|
||||
this.target = (JpaTarget) target;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,63 +9,78 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.rollout;
|
||||
|
||||
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.RolloutProperties;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.slf4j.Logger;
|
||||
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.stereotype.Component;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
/**
|
||||
* Scheduler to schedule the
|
||||
* {@link RolloutManagement#checkRunningRollouts(long)}. The delay between the
|
||||
* checks be be configured using the properties from {@link RolloutProperties}.
|
||||
* Scheduler to schedule the {@link RolloutManagement#handleRollouts()}. The
|
||||
* delay between the checks be be configured using the property from
|
||||
* {#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 {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RolloutScheduler.class);
|
||||
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.scheduler.fixedDelay:2000}";
|
||||
|
||||
@Autowired
|
||||
private SystemManagement systemManagement;
|
||||
private final TenantAware tenantAware;
|
||||
|
||||
@Autowired
|
||||
private RolloutManagement rolloutManagement;
|
||||
private final SystemManagement systemManagement;
|
||||
|
||||
@Autowired
|
||||
private SystemSecurityContext systemSecurityContext;
|
||||
private final RolloutManagement rolloutManagement;
|
||||
|
||||
@Autowired
|
||||
private RolloutProperties rolloutProperties;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
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
|
||||
* 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}.
|
||||
*/
|
||||
@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() {
|
||||
if (!rolloutProperties.getScheduler().isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.debug("rollout schedule checker has been triggered.");
|
||||
|
||||
// run this code in system code privileged to have the necessary
|
||||
// permission to query and create entities.
|
||||
systemSecurityContext.runAsSystem(() -> {
|
||||
final int tasks = systemSecurityContext.runAsSystem(() -> {
|
||||
// workaround eclipselink that is currently not possible to
|
||||
// execute a query without multitenancy if MultiTenant
|
||||
// annotation is used.
|
||||
@@ -75,119 +90,25 @@ public class RolloutScheduler {
|
||||
final List<String> tenants = systemManagement.findTenants();
|
||||
LOGGER.info("Checking rollouts for {} tenants", tenants.size());
|
||||
for (final String tenant : tenants) {
|
||||
tenantAware.runAsTenant(tenant, () -> {
|
||||
final long fixedDelay = rolloutProperties.getScheduler().getFixedDelay();
|
||||
rolloutManagement.checkRunningRollouts(fixedDelay);
|
||||
completionService.submit(() -> tenantAware.runAsTenant(tenant, () -> {
|
||||
rolloutManagement.handleRollouts();
|
||||
return null;
|
||||
});
|
||||
}));
|
||||
}
|
||||
return null;
|
||||
return tenants.size();
|
||||
});
|
||||
|
||||
waitUntilHandlersAreComplete(tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scheduler method called by the spring-async mechanism. Retrieves all
|
||||
* tenants from the {@link SystemManagement#findTenants()} and runs for each
|
||||
* tenant the {@link RolloutManagement#checkStartingRollouts(long)} in the
|
||||
* {@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;
|
||||
});
|
||||
private void waitUntilHandlersAreComplete(final int tasks) {
|
||||
try {
|
||||
for (int i = 0; i < tasks; i++) {
|
||||
completionService.take().get();
|
||||
}
|
||||
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#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;
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActi
|
||||
// scheduled. If the group is empty now, we just finish the group if
|
||||
// there are not actions available for this group.
|
||||
final List<JpaRolloutGroup> findByRolloutGroupParent = rolloutGroupRepository
|
||||
.findByParentAndStatus((JpaRolloutGroup) rolloutGroup, RolloutGroupStatus.SCHEDULED);
|
||||
.findByParentIdAndStatus(rolloutGroup.getId(), RolloutGroupStatus.SCHEDULED);
|
||||
findByRolloutGroupParent.forEach(nextGroup -> {
|
||||
logger.debug("Rolloutgroup {} is finished, starting next group", nextGroup);
|
||||
nextGroup.setStatus(RolloutGroupStatus.FINISHED);
|
||||
|
||||
@@ -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.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.Target;
|
||||
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
|
||||
* 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.
|
||||
*
|
||||
* @param target
|
||||
* the target to verfiy if the given artifact is currently
|
||||
* @param controllerId
|
||||
* 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 Target target, final String sha1Hash) {
|
||||
public static Specification<JpaAction> hasTargetAssignedArtifact(final String controllerId, 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), 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));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -65,6 +65,12 @@ public class RemoteIdEventTest extends AbstractRemoteEventTest {
|
||||
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) {
|
||||
|
||||
final Constructor<?> constructor = Arrays.stream(eventType.getDeclaredConstructors())
|
||||
|
||||
@@ -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.model.Action;
|
||||
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.junit.Test;
|
||||
@@ -46,6 +47,7 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
|
||||
final Target target = testdataFactory.createTarget("Test");
|
||||
generateAction.setTarget(target);
|
||||
generateAction.setDistributionSet(dsA);
|
||||
generateAction.setStatus(Status.RUNNING);
|
||||
final Action action = actionRepository.save(generateAction);
|
||||
|
||||
final TargetAssignDistributionSetEvent assignmentEvent = new TargetAssignDistributionSetEvent(action,
|
||||
|
||||
@@ -8,9 +8,16 @@
|
||||
*/
|
||||
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.model.Action;
|
||||
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.junit.Test;
|
||||
|
||||
@@ -35,7 +42,49 @@ public class ActionEventTest extends AbstractRemoteEntityEventTest<Action> {
|
||||
@Description("Verifies that the action entity reloading by remote updated works")
|
||||
public void testActionUpdatedEvent() {
|
||||
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
|
||||
@@ -43,8 +92,10 @@ public class ActionEventTest extends AbstractRemoteEntityEventTest<Action> {
|
||||
final JpaAction generateAction = new JpaAction();
|
||||
generateAction.setActionType(ActionType.FORCED);
|
||||
final Target target = testdataFactory.createTarget("Test");
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
generateAction.setTarget(target);
|
||||
generateAction.setDistributionSet(testdataFactory.createDistributionSet());
|
||||
generateAction.setDistributionSet(distributionSet);
|
||||
generateAction.setStatus(Status.RUNNING);
|
||||
return actionRepository.save(generateAction);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
@@ -44,6 +46,47 @@ public class RolloutGroupEventTest extends AbstractRemoteEntityEventTest<Rollout
|
||||
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
|
||||
protected RolloutGroup createEntity() {
|
||||
testdataFactory.createTarget(UUID.randomUUID().toString());
|
||||
|
||||
@@ -31,6 +31,8 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@SpringApplicationConfiguration(classes = {
|
||||
org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration.class })
|
||||
public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest {
|
||||
@@ -80,6 +82,9 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
|
||||
@Autowired
|
||||
protected RolloutGroupRepository rolloutGroupRepository;
|
||||
|
||||
@Autowired
|
||||
protected RolloutTargetGroupRepository rolloutTargetGroupRepository;
|
||||
|
||||
@Autowired
|
||||
protected RolloutRepository rolloutRepository;
|
||||
|
||||
@@ -91,7 +96,7 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
|
||||
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
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) {
|
||||
|
||||
@@ -9,23 +9,29 @@
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
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.fail;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
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.jpa.model.JpaArtifact;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
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.WithUser;
|
||||
import org.junit.Test;
|
||||
@@ -35,24 +41,47 @@ import ru.yandex.qatools.allure.annotations.Features;
|
||||
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")
|
||||
@Stories("Artifact Management")
|
||||
public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
/**
|
||||
* Test method for
|
||||
* {@link org.eclipse.hawkbit.repository.ArtifactManagement#createArtifact(java.io.InputStream)}
|
||||
* .
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
@Test
|
||||
@Description("Verifies that management queries react as specfied on calls for non existing entities.")
|
||||
@ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
|
||||
public void nonExistingEntityQueries() throws URISyntaxException {
|
||||
final SoftwareModule module = testdataFactory.createSoftwareModuleOs();
|
||||
|
||||
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
|
||||
@Description("Test if a local artifact can be created by API including metadata.")
|
||||
public void createArtifact() throws NoSuchAlgorithmException, IOException {
|
||||
@@ -205,7 +234,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Artifact result = artifactManagement.createArtifact(new ByteArrayInputStream(random),
|
||||
testdataFactory.createSoftwareModuleOs().getId(), "file1", false);
|
||||
|
||||
try (InputStream fileInputStream = artifactManagement.loadArtifactBinary(result.getSha1Hash())
|
||||
try (InputStream fileInputStream = artifactManagement.loadArtifactBinary(result.getSha1Hash()).get()
|
||||
.getFileInputStream()) {
|
||||
assertTrue("The stored binary matches the given binary",
|
||||
IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream));
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
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.TargetUpdatedEvent;
|
||||
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.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
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.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||
@@ -55,6 +60,75 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
@Autowired
|
||||
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
|
||||
@Description("Controller confirms successfull update with FINISHED status.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@@ -68,7 +142,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
simulateIntermediateStatusOnUpdate(actionId);
|
||||
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||
Action.Status.FINISHED, Action.Status.FINISHED, false);
|
||||
@@ -91,7 +165,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Long actionId = createTargetAndAssignDs();
|
||||
deploymentManagement.cancelAction(actionId);
|
||||
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||
Action.Status.FINISHED, Action.Status.FINISHED, false);
|
||||
@@ -111,7 +185,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Long actionId = createTargetAndAssignDs();
|
||||
|
||||
try {
|
||||
controllerManagament.addCancelActionStatus(
|
||||
controllerManagement.addCancelActionStatus(
|
||||
entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
fail("Expected " + CancelActionNotAllowedException.class.getName());
|
||||
} catch (final CancelActionNotAllowedException e) {
|
||||
@@ -144,7 +218,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
simulateIntermediateStatusOnCancellation(actionId);
|
||||
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||
Action.Status.CANCELED, Action.Status.FINISHED, false);
|
||||
@@ -171,7 +245,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
simulateIntermediateStatusOnCancellation(actionId);
|
||||
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.CANCELED));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.IN_SYNC,
|
||||
Action.Status.CANCELED, Action.Status.CANCELED, false);
|
||||
@@ -199,7 +273,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
simulateIntermediateStatusOnCancellation(actionId);
|
||||
|
||||
controllerManagament.addCancelActionStatus(
|
||||
controllerManagement.addCancelActionStatus(
|
||||
entityFactory.actionStatus().create(actionId).status(Action.Status.CANCEL_REJECTED));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
Action.Status.RUNNING, Action.Status.CANCEL_REJECTED, true);
|
||||
@@ -227,7 +301,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
simulateIntermediateStatusOnCancellation(actionId);
|
||||
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
Action.Status.RUNNING, Action.Status.ERROR, true);
|
||||
@@ -249,22 +323,22 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Step
|
||||
private void simulateIntermediateStatusOnCancellation(final Long actionId) {
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
Action.Status.CANCELING, Action.Status.RUNNING, true);
|
||||
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
Action.Status.CANCELING, Action.Status.DOWNLOAD, true);
|
||||
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
Action.Status.CANCELING, Action.Status.RETRIEVED, true);
|
||||
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addCancelActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
Action.Status.CANCELING, Action.Status.WARNING, true);
|
||||
@@ -272,22 +346,22 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Step
|
||||
private void simulateIntermediateStatusOnUpdate(final Long actionId) {
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
Action.Status.RUNNING, Action.Status.RUNNING, true);
|
||||
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
Action.Status.RUNNING, Action.Status.DOWNLOAD, true);
|
||||
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
Action.Status.RUNNING, Action.Status.RETRIEVED, true);
|
||||
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
Action.Status.RUNNING, Action.Status.WARNING, true);
|
||||
@@ -305,7 +379,10 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final List<ActionStatus> actionStatusList = deploymentManagement.findActionStatusByAction(pageReq, actionId)
|
||||
.getContent();
|
||||
assertThat(actionStatusList.get(actionStatusList.size() - 1).getStatus()).isEqualTo(expectedActionStatus);
|
||||
|
||||
if (actionActive) {
|
||||
assertThat(controllerManagement.findOldestActiveActionByTarget(controllerId).get().getId())
|
||||
.isEqualTo(actionId);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -332,31 +409,31 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(artifact.getSha1Hash()).isEqualTo(artifact2.getSha1Hash());
|
||||
|
||||
assertThat(
|
||||
controllerManagament.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact.getSha1Hash()))
|
||||
controllerManagement.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact.getSha1Hash()))
|
||||
.isFalse();
|
||||
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getAssignedEntity().iterator()
|
||||
.next();
|
||||
assertThat(
|
||||
controllerManagament.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact.getSha1Hash()))
|
||||
controllerManagement.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact.getSha1Hash()))
|
||||
.isTrue();
|
||||
assertThat(
|
||||
controllerManagament.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact2.getSha1Hash()))
|
||||
controllerManagement.hasTargetArtifactAssigned(savedTarget.getControllerId(), artifact2.getSha1Hash()))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Register a controller which does not exist")
|
||||
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();
|
||||
|
||||
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(targetRepository.count()).as("Only 1 target should be registred").isEqualTo(1L);
|
||||
|
||||
// throws exception
|
||||
try {
|
||||
controllerManagament.findOrRegisterTargetIfItDoesNotexist("", null);
|
||||
controllerManagement.findOrRegisterTargetIfItDoesNotexist("", null);
|
||||
fail("should fail as target does not exist");
|
||||
} catch (final ConstraintViolationException e) {
|
||||
|
||||
@@ -375,19 +452,19 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Long actionId = createTargetAndAssignDs();
|
||||
|
||||
// test and verify
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING,
|
||||
Action.Status.RUNNING, Action.Status.RUNNING, true);
|
||||
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.ERROR));
|
||||
assertActionStatus(actionId, TestdataFactory.DEFAULT_CONTROLLER_ID, TargetUpdateStatus.ERROR,
|
||||
Action.Status.ERROR, Action.Status.ERROR, false);
|
||||
|
||||
// try with disabled late feedback
|
||||
repositoryProperties.setRejectActionStatusForClosedAction(true);
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
|
||||
// test
|
||||
@@ -397,7 +474,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
// try with enabled late feedback - should not make a difference as it
|
||||
// only allows intermediate feedbacks and not multiple close
|
||||
repositoryProperties.setRejectActionStatusForClosedAction(false);
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
|
||||
// test
|
||||
@@ -422,7 +499,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
// try with disabled late feedback
|
||||
repositoryProperties.setRejectActionStatusForClosedAction(true);
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
|
||||
// test
|
||||
@@ -432,7 +509,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
// try with enabled late feedback - should not make a difference as it
|
||||
// only allows intermediate feedbacks and not multiple close
|
||||
repositoryProperties.setRejectActionStatusForClosedAction(false);
|
||||
controllerManagament
|
||||
controllerManagement
|
||||
.addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.FINISHED));
|
||||
|
||||
// test
|
||||
@@ -458,7 +535,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
final Action action = prepareFinishedUpdate();
|
||||
|
||||
controllerManagament.addUpdateActionStatus(
|
||||
controllerManagement.addUpdateActionStatus(
|
||||
entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING));
|
||||
|
||||
// nothing changed as "feedback after close" is disabled
|
||||
@@ -483,7 +560,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
repositoryProperties.setRejectActionStatusForClosedAction(false);
|
||||
|
||||
Action action = prepareFinishedUpdate();
|
||||
action = controllerManagament.addUpdateActionStatus(
|
||||
action = controllerManagement.addUpdateActionStatus(
|
||||
entityFactory.actionStatus().create(action.getId()).status(Action.Status.RUNNING));
|
||||
|
||||
// nothing changed as "feedback after close" is disabled
|
||||
@@ -512,7 +589,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
private void addAttributeAndVerify(final String controllerId) {
|
||||
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(1);
|
||||
testData.put("test1", "testdata1");
|
||||
controllerManagament.updateControllerAttributes(controllerId, testData);
|
||||
controllerManagement.updateControllerAttributes(controllerId, testData);
|
||||
|
||||
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get();
|
||||
assertThat(target.getTargetInfo().getControllerAttributes()).as("Controller Attributes are wrong")
|
||||
@@ -523,7 +600,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
private void addSecondAttributeAndVerify(final String controllerId) {
|
||||
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(2);
|
||||
testData.put("test2", "testdata20");
|
||||
controllerManagament.updateControllerAttributes(controllerId, testData);
|
||||
controllerManagement.updateControllerAttributes(controllerId, testData);
|
||||
|
||||
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get();
|
||||
testData.put("test1", "testdata1");
|
||||
@@ -536,7 +613,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Map<String, String> testData = Maps.newHashMapWithExpectedSize(2);
|
||||
testData.put("test1", "testdata12");
|
||||
|
||||
controllerManagament.updateControllerAttributes(controllerId, testData);
|
||||
controllerManagement.updateControllerAttributes(controllerId, testData);
|
||||
|
||||
final Target target = targetManagement.findTargetByControllerIDWithDetails(controllerId).get();
|
||||
testData.put("test2", "testdata20");
|
||||
|
||||
@@ -752,7 +752,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
.as("Installed distribution set of action should be null").isNotNull();
|
||||
|
||||
final Page<Action> updAct = actionRepository.findByDistributionSetId(pageReq, dsA.getId());
|
||||
controllerManagament.addUpdateActionStatus(
|
||||
controllerManagement.addUpdateActionStatus(
|
||||
entityFactory.actionStatus().create(updAct.getContent().get(0).getId()).status(Status.FINISHED));
|
||||
|
||||
targ = targetManagement.findTargetByControllerID(targ.getControllerId()).get();
|
||||
|
||||
@@ -139,7 +139,7 @@ public class ReportManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target createTarget = testdataFactory.createTarget("t" + month);
|
||||
final DistributionSetAssignmentResult result = assignDistributionSet(distributionSet,
|
||||
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.");
|
||||
}
|
||||
DataReportSeries<LocalDate> feedbackReceivedOverTime = reportManagement
|
||||
@@ -160,7 +160,7 @@ public class ReportManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target createTarget = testdataFactory.createTarget("t2" + month);
|
||||
final DistributionSetAssignmentResult result = assignDistributionSet(distributionSet,
|
||||
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.");
|
||||
}
|
||||
feedbackReceivedOverTime = reportManagement.feedbackReceivedOverTime(DateTypes.perMonth(), from, to);
|
||||
|
||||