Initial Contribution of the rollout-management feature
- Repository functionality for rollout, rolloutgroup entities - Rollout scheduler to watch and handle running rollouts and start next group of rollout - Vaadin view to administrate rollouts and reflect the current rollout status - REST resources to cover rollout creation, updating, starting, pausing and resuming Signed-off-by: Michael Hirsch <michael.hirsch@bosch-si.com>
This commit is contained in:
@@ -9,4 +9,39 @@ The repository is in charge for managing the meta data of the update server, e.g
|
||||
$ mvn clean install
|
||||
----
|
||||
|
||||
Note, in order to build correctly in your IDE, you have to add ./target/generated-sources/apt to your build path.
|
||||
Note, in order to build correctly in your IDE, you have to add ./target/generated-sources/apt to your build path.
|
||||
|
||||
#Concepts
|
||||
|
||||
## Rollout
|
||||
A rollout consists of the distribution set and a target-query-filter which defines the targets to be updated in this rollout. The targets within this filter are split up in configured amount of _Deployment Groups_ at creation time.
|
||||
|
||||
When starting a rollout, for all targets within this rollout deployment actions will be created. The deployment actions of the first group will be started immediately all other deployment actions will be scheduled.
|
||||
|
||||
> Due rollouts might include a large number of targets and deployment group, creation as well as starting a rollout might take some time and therefore the creation and starting of an rollout is executed asynchronously. The creation and starting progress is reflected by the rollout's status attribute
|
||||
|
||||
### Rollout Creation
|
||||
The targets reflected by the target-query-filter is interpreted at creation time. Only targets which have been created at that time are included in the rollout. Targets which are created after the rollout creation will not be included. At rollout creation time all necessary deployment groups containing the targets will be created. Dynamically adding targets to a created or running rollout is currently not supported.
|
||||
|
||||
### Rollout Starting
|
||||
The rollout is using the same concept based on _deployment acionts_ per target. All necessary _deployment acionts_ will be created at starting point but not all _deployment acionts_ will be set to running. Only the _deployment acionts_ of the first _deployment group_ is set to running, all other actions are created in _scheduled_ state.
|
||||
If targets having not finished _deployment actions_ at rollout starting time, these action are tried to cancel (state: canceling). Scheduled actions from another rollout are canceled directly on server side because they were never running before and can be safely canceled.
|
||||
|
||||
>Multiple rollouts can be running at the same time, but if the rollouts effect the same targets they will interfer the targets _deployment actions_ e.g. canceling/cancel already running/scheduled _deployment actions_.
|
||||
|
||||
### Deployment Group Condition/Action
|
||||
#### Success Condition/Action
|
||||
A _success condition_ defines when a deployment group is interpreted as success and triggers the _success action_. E.g. a threshold of successfully updated targets within the group.
|
||||
|
||||
The _success action_ is executed if the _success_condition_ is fullfilled. Currently only the _success action_ starting the next following deployment group is available.
|
||||
|
||||
#### Error Condition/Action
|
||||
A _error condition_ defines when a deployment group is interpreted as failure and triggers the _error action_. E.g. a threshold of update failures of targets within the group.
|
||||
|
||||
The _error action_ is executed if the _error condition_ is fullfilled. Currently only the _error action_ pausing the whole rollout is available, a paused rollout can be resumed by a user.
|
||||
|
||||
### Rollout Scheduler
|
||||
The rollout is managed by a scheduler task which is checking for rollouts in _running_ state. This is done atomic by updating the row in the database, so only the rollouts are checked and processed for the current instance to avoid that multiple instances are processing a rollout.
|
||||
|
||||
>Due the scheduler, it might take some time until a rollouts is processed and switching is state or starting the next deployment group.
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.transaction.Transaction;
|
||||
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
@@ -46,7 +47,8 @@ public class MultiTenantJpaTransactionManager extends JpaTransactionManager {
|
||||
if (!definition.getName().startsWith(SystemManagement.class.getCanonicalName() + ".findTenants")
|
||||
&& !definition.getName().startsWith(SystemManagement.class.getCanonicalName() + ".deleteTenant")
|
||||
&& !definition.getName()
|
||||
.startsWith(SystemManagement.class.getCanonicalName() + ".currentTenantKeyGenerator")) {
|
||||
.startsWith(SystemManagement.class.getCanonicalName() + ".currentTenantKeyGenerator")
|
||||
&& !definition.getName().startsWith(RolloutManagement.class.getCanonicalName() + ".rolloutScheduler")) {
|
||||
|
||||
final String currentTenant = tenantAware.getCurrentTenant();
|
||||
if (currentTenant == null) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.Map;
|
||||
|
||||
import org.eclipse.hawkbit.aspects.ExceptionMappingAspectHandler;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.model.helper.AfterTransactionCommitExecutorHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.CacheManagerHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.PollConfigurationHelper;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SecurityTokenGeneratorHolder;
|
||||
@@ -112,6 +113,16 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
return CacheManagerHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the singleton instance of the
|
||||
* {@link AfterTransactionCommitExecutorHolder}
|
||||
*/
|
||||
@Bean
|
||||
public AfterTransactionCommitExecutorHolder afterTransactionCommitExecutorHolder() {
|
||||
return AfterTransactionCommitExecutorHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the validation processor bean.
|
||||
*
|
||||
|
||||
@@ -28,6 +28,8 @@ public final class CacheKeys {
|
||||
* event.
|
||||
*/
|
||||
public static final String DOWNLOAD_PROGRESS_PERCENT = "download.progress.percent";
|
||||
public static final String ROLLOUT_GROUP_CREATED = "rollout.group.created";
|
||||
public static final String ROLLOUT_GROUP_TOTAL = "rollout.group.total";
|
||||
|
||||
/**
|
||||
* utility class only private constructor.
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
package org.eclipse.hawkbit.cache;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.event.DownloadProgressEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.RolloutGroupCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.Cache;
|
||||
@@ -70,6 +72,43 @@ public class CacheWriteNotify {
|
||||
eventBus.post(new DownloadProgressEvent(tenantAware.getCurrentTenant(), statusId, progressPercent));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the {@link CacheKeys#ROLLOUT_GROUP_CREATED} and
|
||||
* {@link CacheKeys#ROLLOUT_GROUP_TOTAL} into the cache and notfies the
|
||||
* {@link EventBus} with a {@link RolloutGroupCreatedEvent}.
|
||||
*
|
||||
* @param revision
|
||||
* the revision of the event
|
||||
* @param rolloutId
|
||||
* the ID of the rollout the group has been created
|
||||
* @param rolloutGroupId
|
||||
* the ID of the rollout group which has been created
|
||||
* @param totalRolloutGroup
|
||||
* the total number of rollout groups for this rollout
|
||||
* @param createdRolloutGroup
|
||||
* the number of already created groups of the rollout
|
||||
*/
|
||||
public void rolloutGroupCreated(final long revision, final Long rolloutId, final Long rolloutGroupId,
|
||||
final int totalRolloutGroup, final int createdRolloutGroup) {
|
||||
|
||||
final Cache cache = cacheManager.getCache(Rollout.class.getName());
|
||||
final String cacheKeyGroupTotal = CacheKeys.entitySpecificCacheKey(String.valueOf(rolloutId),
|
||||
CacheKeys.ROLLOUT_GROUP_TOTAL);
|
||||
final String cacheKeyGroupCreated = CacheKeys.entitySpecificCacheKey(String.valueOf(rolloutId),
|
||||
CacheKeys.ROLLOUT_GROUP_CREATED);
|
||||
if (createdRolloutGroup < totalRolloutGroup) {
|
||||
cache.put(cacheKeyGroupTotal, totalRolloutGroup);
|
||||
cache.put(cacheKeyGroupCreated, createdRolloutGroup);
|
||||
} else {
|
||||
// in case we reached progress 100 delete the cache value again
|
||||
// because otherwise he will keep there forever
|
||||
cache.evict(cacheKeyGroupTotal);
|
||||
cache.evict(cacheKeyGroupCreated);
|
||||
}
|
||||
eventBus.post(new RolloutGroupCreatedEvent(tenantAware.getCurrentTenant(), revision, rolloutId, rolloutGroupId,
|
||||
totalRolloutGroup, createdRolloutGroup));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cacheManager
|
||||
* the cacheManager to set
|
||||
|
||||
@@ -144,4 +144,5 @@ public class EntityChangeEventListener {
|
||||
private boolean isTargetInfoNew(final Object targetInfo) {
|
||||
return ((TargetInfo) targetInfo).isNew();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* 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.eventbus;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.event.AbstractPropertyChangeEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.ActionCreatedEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.ActionPropertyChangeEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.RolloutGroupPropertyChangeEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.RolloutPropertyChangeEvent;
|
||||
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.helper.AfterTransactionCommitExecutorHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.EventBusHolder;
|
||||
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
|
||||
import org.eclipse.persistence.internal.sessions.ObjectChangeSet;
|
||||
import org.eclipse.persistence.queries.UpdateObjectQuery;
|
||||
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* Listens to change in property values of an entity.
|
||||
*
|
||||
*/
|
||||
public class EntityPropertyChangeListener extends DescriptorEventAdapter {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.eclipse.persistence.descriptors.DescriptorEventAdapter#postInsert
|
||||
* (org.eclipse.persistence.descriptors.DescriptorEvent)
|
||||
*/
|
||||
@Override
|
||||
public void postInsert(final DescriptorEvent event) {
|
||||
if (event.getObject().getClass().equals(Action.class)) {
|
||||
final Action action = (Action) event.getObject();
|
||||
if (action.getRollout() != null) {
|
||||
final EventBus eventBus = getEventBus();
|
||||
final AfterTransactionCommitExecutor afterCommit = getAfterTransactionCommmitExecutor();
|
||||
afterCommit.afterCommit(() -> eventBus.post(new ActionCreatedEvent(action)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.eclipse.persistence.descriptors.DescriptorEventAdapter#postUpdate
|
||||
* (org.eclipse.persistence.descriptors.DescriptorEvent)
|
||||
*/
|
||||
@Override
|
||||
public void postUpdate(final DescriptorEvent event) {
|
||||
if (event.getObject().getClass().equals(Action.class)) {
|
||||
getAfterTransactionCommmitExecutor().afterCommit(
|
||||
() -> getEventBus()
|
||||
.post(new ActionPropertyChangeEvent((Action) event.getObject(), getChangeSet(Action.class,
|
||||
event))));
|
||||
} else if (event.getObject().getClass().equals(Rollout.class)) {
|
||||
getAfterTransactionCommmitExecutor().afterCommit(
|
||||
() -> getEventBus().post(
|
||||
new RolloutPropertyChangeEvent((Rollout) event.getObject(), getChangeSet(Rollout.class,
|
||||
event))));
|
||||
} else if (event.getObject().getClass().equals(RolloutGroup.class)) {
|
||||
getAfterTransactionCommmitExecutor().afterCommit(
|
||||
() -> getEventBus().post(
|
||||
new RolloutGroupPropertyChangeEvent((RolloutGroup) event.getObject(), getChangeSet(
|
||||
RolloutGroup.class, event))));
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends BaseEntity> Map<String, AbstractPropertyChangeEvent<T>.Values> getChangeSet(
|
||||
final Class<T> clazz, final DescriptorEvent event) {
|
||||
final T rolloutGroup = clazz.cast(event.getObject());
|
||||
final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
|
||||
return changeSet
|
||||
.getChanges()
|
||||
.stream()
|
||||
.filter(record -> record instanceof DirectToFieldChangeRecord)
|
||||
.map(record -> (DirectToFieldChangeRecord) record)
|
||||
.collect(
|
||||
Collectors.toMap(record -> record.getAttribute(), record -> new AbstractPropertyChangeEvent<T>(
|
||||
rolloutGroup, null).new Values(record.getOldValue(), record.getNewValue())));
|
||||
}
|
||||
|
||||
private AfterTransactionCommitExecutor getAfterTransactionCommmitExecutor() {
|
||||
return AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit();
|
||||
}
|
||||
|
||||
private EventBus getEventBus() {
|
||||
return EventBusHolder.getInstance().getEventBus();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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.eventbus.event;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||
|
||||
/**
|
||||
* Property change event.
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public class AbstractPropertyChangeEvent<E extends BaseEntity> extends AbstractBaseEntityEvent<E> {
|
||||
|
||||
private static final long serialVersionUID = -3671601415138242311L;
|
||||
private final transient Map<String, Values> changeSet;
|
||||
|
||||
/**
|
||||
* Initialize base entity and property changed with old and new value.
|
||||
*
|
||||
* @param baseEntity
|
||||
* entity changed
|
||||
* @param changeSetValues
|
||||
* details of properties changed and old value and new value of
|
||||
* the changed properties
|
||||
*/
|
||||
public AbstractPropertyChangeEvent(final E baseEntity, final Map<String, Values> changeSetValues) {
|
||||
super(baseEntity);
|
||||
this.changeSet = changeSetValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the changeSet
|
||||
*/
|
||||
public Map<String, Values> getChangeSet() {
|
||||
return changeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Carries old value and new value of a property .
|
||||
*
|
||||
*/
|
||||
public class Values {
|
||||
private final Object oldValue;
|
||||
private final Object newValue;
|
||||
|
||||
/**
|
||||
* Initialize old value and new changes value of property.
|
||||
*
|
||||
* @param oldValue
|
||||
* old value before change
|
||||
* @param newValue
|
||||
* new value after change
|
||||
*/
|
||||
public Values(final Object oldValue, final Object newValue) {
|
||||
super();
|
||||
this.oldValue = oldValue;
|
||||
this.newValue = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the oldValue
|
||||
*/
|
||||
public Object getOldValue() {
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the newValue
|
||||
*/
|
||||
public Object getNewValue() {
|
||||
return newValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.eventbus.event;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
|
||||
/**
|
||||
* Defines the {@link AbstractBaseEntityEvent} of creating a new {@link Action}.
|
||||
*/
|
||||
public class ActionCreatedEvent extends AbstractBaseEntityEvent<Action> {
|
||||
private static final long serialVersionUID = 181780358321768629L;
|
||||
|
||||
/**
|
||||
* @param action
|
||||
*/
|
||||
public ActionCreatedEvent(final Action action) {
|
||||
super(action);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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.eventbus.event;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
|
||||
/**
|
||||
* Defines the {@link AbstractPropertyChangeEvent} of {@link Action}.
|
||||
*/
|
||||
public class ActionPropertyChangeEvent extends AbstractPropertyChangeEvent<Action> {
|
||||
private static final long serialVersionUID = 181780358321768629L;
|
||||
|
||||
/**
|
||||
* @param action
|
||||
* @param changeSetValues
|
||||
*/
|
||||
public ActionPropertyChangeEvent(final Action action,
|
||||
final Map<String, AbstractPropertyChangeEvent<Action>.Values> changeSetValues) {
|
||||
super(action, changeSetValues);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* 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.eventbus.event;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.EventSubscriber;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import com.google.common.eventbus.AllowConcurrentEvents;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
/**
|
||||
* Collects and merges fine grained events to more generic events and push them
|
||||
* in a fixed delay on the events bus. This helps for code which are not
|
||||
* interested in all fine grained events, e.g. UI code. The UI code is not
|
||||
* interested in handling the flood of events, so collecting the events and
|
||||
* merge them to one event together and post them in a fixed interval is easier
|
||||
* to consume e.g. for push notifcations on UI.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
@EventSubscriber
|
||||
public class EventMerger {
|
||||
|
||||
private static final Set<RolloutEventKey> rolloutEvents = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<RolloutEventKey> rolloutGroupEvents = ConcurrentHashMap.newKeySet();
|
||||
|
||||
@Autowired
|
||||
private EventBus eventBus;
|
||||
|
||||
/**
|
||||
* Checks if there are events to publish in the fixed interval.
|
||||
*/
|
||||
@Scheduled(initialDelay = 10000, fixedDelay = 2000)
|
||||
public void rolloutEventScheduler() {
|
||||
final Iterator<RolloutEventKey> rolloutIterator = rolloutEvents.iterator();
|
||||
while (rolloutIterator.hasNext()) {
|
||||
final RolloutEventKey eventKey = rolloutIterator.next();
|
||||
eventBus.post(new RolloutChangeEvent(1, eventKey.tenant, eventKey.rolloutId));
|
||||
rolloutIterator.remove();
|
||||
}
|
||||
|
||||
final Iterator<RolloutEventKey> rolloutGroupIterator = rolloutGroupEvents.iterator();
|
||||
while (rolloutGroupIterator.hasNext()) {
|
||||
final RolloutEventKey eventKey = rolloutGroupIterator.next();
|
||||
eventBus.post(new RolloutGroupChangeEvent(1, eventKey.tenant, eventKey.rolloutId, eventKey.rolloutGroupId));
|
||||
rolloutGroupIterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the event bus to retrieve all necessary events to collect and
|
||||
* merge.
|
||||
*
|
||||
* @param event
|
||||
* the event on the event bus
|
||||
*/
|
||||
@Subscribe
|
||||
@AllowConcurrentEvents
|
||||
public void onEvent(final Event event) {
|
||||
Long rolloutId = null;
|
||||
Long rolloutGroupId = null;
|
||||
if (event instanceof ActionCreatedEvent) {
|
||||
rolloutId = getRolloutId(((ActionCreatedEvent) event).getEntity().getRollout());
|
||||
rolloutGroupId = getRolloutGroupId(((ActionCreatedEvent) event).getEntity().getRolloutGroup());
|
||||
} else if (event instanceof ActionPropertyChangeEvent) {
|
||||
rolloutId = getRolloutId(((ActionPropertyChangeEvent) event).getEntity().getRollout());
|
||||
rolloutGroupId = getRolloutGroupId(((ActionPropertyChangeEvent) event).getEntity().getRolloutGroup());
|
||||
} else if (event instanceof RolloutPropertyChangeEvent) {
|
||||
rolloutId = ((RolloutPropertyChangeEvent) event).getEntity().getId();
|
||||
} else if (event instanceof RolloutGroupCreatedEvent) {
|
||||
rolloutId = ((RolloutGroupCreatedEvent) event).getRolloutId();
|
||||
rolloutGroupId = ((RolloutGroupCreatedEvent) event).getRolloutGroupId();
|
||||
} else if (event instanceof RolloutGroupPropertyChangeEvent) {
|
||||
final RolloutGroup rolloutGroup = ((RolloutGroupPropertyChangeEvent) event).getEntity();
|
||||
rolloutId = rolloutGroup.getRollout().getId();
|
||||
rolloutGroupId = rolloutGroup.getId();
|
||||
}
|
||||
|
||||
if (rolloutId != null) {
|
||||
rolloutEvents.add(new RolloutEventKey(rolloutId, event.getTenant()));
|
||||
if (rolloutGroupId != null) {
|
||||
rolloutGroupEvents.add(new RolloutEventKey(rolloutId, rolloutGroupId, event.getTenant()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Long getRolloutGroupId(final RolloutGroup rolloutGroup) {
|
||||
if (rolloutGroup != null) {
|
||||
return rolloutGroup.getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Long getRolloutId(final Rollout rollout) {
|
||||
if (rollout != null) {
|
||||
return rollout.getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rollout key in the concurrent set to be hold.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
private static final class RolloutEventKey {
|
||||
private final Long rolloutId;
|
||||
private final String tenant;
|
||||
private final Long rolloutGroupId;
|
||||
|
||||
private RolloutEventKey(final Long rolloutId, final Long rolloutGroupId, final String tenant) {
|
||||
this.rolloutGroupId = rolloutGroupId;
|
||||
this.rolloutId = rolloutId;
|
||||
this.tenant = tenant;
|
||||
}
|
||||
|
||||
private RolloutEventKey(final Long rolloutId, final String tenant) {
|
||||
this(rolloutId, null, tenant);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {// NOSONAR - as this is generated
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((rolloutGroupId == null) ? 0 : rolloutGroupId.hashCode());
|
||||
result = prime * result + ((rolloutId == null) ? 0 : rolloutId.hashCode());
|
||||
result = prime * result + ((tenant == null) ? 0 : tenant.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {// NOSONAR - as this is
|
||||
// generated
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final RolloutEventKey other = (RolloutEventKey) obj;
|
||||
if (rolloutGroupId == null) {
|
||||
if (other.rolloutGroupId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!rolloutGroupId.equals(other.rolloutGroupId)) {
|
||||
return false;
|
||||
}
|
||||
if (rolloutId == null) {
|
||||
if (other.rolloutId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!rolloutId.equals(other.rolloutId)) {
|
||||
return false;
|
||||
}
|
||||
if (tenant == null) {
|
||||
if (other.tenant != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!tenant.equals(other.tenant)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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.eventbus.event;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.event.AbstractEvent;
|
||||
|
||||
/**
|
||||
* Event declaration for the UI to notify the UI that a rollout has been
|
||||
* changed.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
public class RolloutChangeEvent extends AbstractEvent {
|
||||
|
||||
private final Long rolloutId;
|
||||
|
||||
/**
|
||||
* @param revision
|
||||
* the revision of the event
|
||||
* @param tenant
|
||||
* the tenant of the event
|
||||
* @param rolloutId
|
||||
* the ID of the rollout which has been changed
|
||||
*/
|
||||
public RolloutChangeEvent(final long revision, final String tenant, final Long rolloutId) {
|
||||
super(revision, tenant);
|
||||
this.rolloutId = rolloutId;
|
||||
}
|
||||
|
||||
public Long getRolloutId() {
|
||||
return rolloutId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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.eventbus.event;
|
||||
|
||||
/**
|
||||
* Event declaration for the UI to notify the UI that a rollout has been
|
||||
* changed.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
public class RolloutGroupChangeEvent extends AbstractEvent {
|
||||
|
||||
private final Long rolloutId;
|
||||
private final Long rolloutGroupId;
|
||||
|
||||
/**
|
||||
* @param revision
|
||||
* the revision of the event
|
||||
* @param tenant
|
||||
* the tenant of the event
|
||||
* @param rolloutId
|
||||
* the ID of the rollout which has been changed
|
||||
* @param rolloutGroupId
|
||||
* the ID of the rollout group which has been changed
|
||||
*/
|
||||
public RolloutGroupChangeEvent(final long revision, final String tenant, final Long rolloutId,
|
||||
final Long rolloutGroupId) {
|
||||
super(revision, tenant);
|
||||
this.rolloutId = rolloutId;
|
||||
this.rolloutGroupId = rolloutGroupId;
|
||||
}
|
||||
|
||||
public Long getRolloutId() {
|
||||
return rolloutId;
|
||||
}
|
||||
|
||||
public Long getRolloutGroupId() {
|
||||
return rolloutGroupId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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.eventbus.event;
|
||||
|
||||
/**
|
||||
* Event definition which is been published in case a rollout group has been
|
||||
* created for a specific rollout.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
public class RolloutGroupCreatedEvent extends AbstractDistributedEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Long rolloutId;
|
||||
private final Long rolloutGroupId;
|
||||
private final int totalRolloutGroup;
|
||||
private final int createdRolloutGroup;
|
||||
|
||||
/**
|
||||
* Creating a new rollout group created event for a specific rollout.
|
||||
*
|
||||
* @param tenant
|
||||
* the tenant of this event
|
||||
* @param revision
|
||||
* the revision of the event
|
||||
* @param rolloutId
|
||||
* the ID of the rollout the group has been created
|
||||
* @param totalRolloutGroup
|
||||
* the total number of rollout groups for this rollout
|
||||
* @param createdRolloutGroup
|
||||
* the number of already created groups of the rollout
|
||||
*/
|
||||
public RolloutGroupCreatedEvent(final String tenant, final long revision, final Long rolloutId,
|
||||
final Long rolloutGroupId, final int totalRolloutGroup, final int createdRolloutGroup) {
|
||||
super(revision, tenant);
|
||||
this.rolloutId = rolloutId;
|
||||
this.rolloutGroupId = rolloutGroupId;
|
||||
this.totalRolloutGroup = totalRolloutGroup;
|
||||
this.createdRolloutGroup = createdRolloutGroup;
|
||||
|
||||
}
|
||||
|
||||
public Long getRolloutId() {
|
||||
return rolloutId;
|
||||
}
|
||||
|
||||
public int getTotalRolloutGroup() {
|
||||
return totalRolloutGroup;
|
||||
}
|
||||
|
||||
public int getCreatedRolloutGroup() {
|
||||
return createdRolloutGroup;
|
||||
}
|
||||
|
||||
public Long getRolloutGroupId() {
|
||||
return rolloutGroupId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.eventbus.event;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
|
||||
/**
|
||||
* Defines the {@link AbstractPropertyChangeEvent} of {@link RolloutGroup}.
|
||||
*/
|
||||
public class RolloutGroupPropertyChangeEvent extends AbstractPropertyChangeEvent<RolloutGroup> {
|
||||
|
||||
private static final long serialVersionUID = 4026477044419472686L;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param rolloutGroup
|
||||
* @param changeSetValues
|
||||
*/
|
||||
public RolloutGroupPropertyChangeEvent(final RolloutGroup rolloutGroup,
|
||||
final Map<String, AbstractPropertyChangeEvent<RolloutGroup>.Values> changeSetValues) {
|
||||
super(rolloutGroup, changeSetValues);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.eventbus.event;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
|
||||
/**
|
||||
* Defines the {@link AbstractPropertyChangeEvent} of {@link Rollout}.
|
||||
*/
|
||||
public class RolloutPropertyChangeEvent extends AbstractPropertyChangeEvent<Rollout> {
|
||||
private static final long serialVersionUID = 1056221355466373514L;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param rollout
|
||||
* @param changeSetValues
|
||||
*/
|
||||
public RolloutPropertyChangeEvent(final Rollout rollout,
|
||||
final Map<String, AbstractPropertyChangeEvent<Rollout>.Values> changeSetValues) {
|
||||
super(rollout, changeSetValues);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class TargetAssignDistributionSetEvent {
|
||||
public class TargetAssignDistributionSetEvent extends AbstractEvent {
|
||||
|
||||
private final Collection<SoftwareModule> softwareModules;
|
||||
private final String controllerId;
|
||||
@@ -29,6 +29,10 @@ public class TargetAssignDistributionSetEvent {
|
||||
/**
|
||||
* Creates a new {@link TargetAssignDistributionSetEvent}.
|
||||
*
|
||||
* @param revision
|
||||
* the revision of the event
|
||||
* @param tenant
|
||||
* the tenant of the event
|
||||
* @param controllerId
|
||||
* the ID of the controller
|
||||
* @param actionId
|
||||
@@ -38,8 +42,9 @@ public class TargetAssignDistributionSetEvent {
|
||||
* @param targetAdress
|
||||
* the targetAdress of the target
|
||||
*/
|
||||
public TargetAssignDistributionSetEvent(final String controllerId, final Long actionId,
|
||||
final Collection<SoftwareModule> softwareModules, final URI targetAdress) {
|
||||
public TargetAssignDistributionSetEvent(final long revision, final String tenant, final String controllerId,
|
||||
final Long actionId, final Collection<SoftwareModule> softwareModules, final URI targetAdress) {
|
||||
super(revision, tenant);
|
||||
this.controllerId = controllerId;
|
||||
this.actionId = actionId;
|
||||
this.softwareModules = softwareModules;
|
||||
|
||||
@@ -14,8 +14,6 @@ import org.eclipse.hawkbit.repository.model.Target;
|
||||
* Defines the {@link AbstractBaseEntityEvent} of creating a new {@link Target}.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class TargetCreatedEvent extends AbstractBaseEntityEvent<Target> {
|
||||
|
||||
|
||||
@@ -12,9 +12,13 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -30,10 +34,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
/**
|
||||
* {@link Action} repository.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public interface ActionRepository extends BaseEntityRepository<Action, Long>, JpaSpecificationExecutor<Action> {
|
||||
@@ -187,6 +187,47 @@ public interface ActionRepository extends BaseEntityRepository<Action, Long>, Jp
|
||||
void setToInactive(@Param("keySet") List<Action> keySet, @Param("targetsIds") List<Long> targetsIds);
|
||||
|
||||
/**
|
||||
* Switches the status of actions from one specific status into another,
|
||||
* only if the actions are in a specific status. This should be a atomar
|
||||
* operation.
|
||||
*
|
||||
* @param statusToSet
|
||||
* the new status the actions should get
|
||||
* @param targetIds
|
||||
* the IDs of the targets of the actions which are affected
|
||||
* @param active
|
||||
* the active flag of the actions which should be affected
|
||||
* @param currentStatus
|
||||
* the current status of the actions which are affected
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("UPDATE Action a SET a.status = :statusToSet WHERE a.target IN :targetsIds AND a.active = :active AND a.status = :currentStatus AND a.distributionSet.requiredMigrationStep = false")
|
||||
void switchStatus(@Param("statusToSet") Action.Status statusToSet, @Param("targetsIds") List<Long> targetIds,
|
||||
@Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus);
|
||||
|
||||
/**
|
||||
* Switches the status of actions from one specific status into another,
|
||||
* only if the actions are in a specific status. This should be a atomar
|
||||
* operation.
|
||||
*
|
||||
* @param statusToSet
|
||||
* the new status the actions should get
|
||||
* @param rollout
|
||||
* the rollout of the actions which are affected
|
||||
* @param active
|
||||
* the active flag of the actions which should be affected
|
||||
* @param currentStatus
|
||||
* the current status of the actions which are affected
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("UPDATE Action a SET a.status = :statusToSet WHERE a.rollout = :rollout AND a.active = :active AND a.status = :currentStatus")
|
||||
void switchStatus(@Param("statusToSet") Action.Status statusToSet, @Param("rollout") Rollout rollout,
|
||||
@Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus);
|
||||
|
||||
/**
|
||||
*
|
||||
* Retrieves all {@link Action}s which are active and referring to the given
|
||||
* target Ids and distribution set required migration step.
|
||||
*
|
||||
@@ -234,7 +275,139 @@ public interface ActionRepository extends BaseEntityRepository<Action, Long>, Jp
|
||||
*
|
||||
* @param distributionSet
|
||||
* DistributionSet to count the {@link Action}s from
|
||||
* @return the count of actions referring to the given target
|
||||
* @return the count of actions referring to the given distributionSet
|
||||
*/
|
||||
Long countByDistributionSet(DistributionSet distributionSet);
|
||||
|
||||
/**
|
||||
* Counts all {@link Action}s referring to the given rollout.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout to count the {@link Action}s from
|
||||
* @return the count of actions referring to the given rollout
|
||||
*/
|
||||
Long countByRollout(Rollout rollout);
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout and rolloutgroup which
|
||||
* are currently not in the given status. An in-clause statement does not
|
||||
* work with the spring-data, so this is specific usecase regarding to the
|
||||
* rollout-management to find out actions which are not in specific states.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the actions are belong to
|
||||
* @param rolloutGroup
|
||||
* the rolloutgroup the actions are belong to
|
||||
* @param notStatus1
|
||||
* the status the action should not have
|
||||
* @param notStatus2
|
||||
* the status the action should not have
|
||||
* @param notStatus3
|
||||
* the status the action should not have
|
||||
* @return the count of actions referring the rollout and rolloutgroup and
|
||||
* are not in given states
|
||||
*/
|
||||
Long countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(Rollout rollout, RolloutGroup rolloutGroup,
|
||||
Status notStatus1, Status notStatus2, Status notStatus3);
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout and rolloutgroup.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the actions belong to
|
||||
* @param rolloutGroup
|
||||
* the rolloutgroup the actions belong to
|
||||
* @return the count of actions referring to a rollout and rolloutgroup
|
||||
*/
|
||||
Long countByRolloutAndRolloutGroup(Rollout rollout, RolloutGroup rolloutGroup);
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout, rolloutgroup and status.
|
||||
*
|
||||
* @param rolloutId
|
||||
* the ID of rollout the actions belong to
|
||||
* @param rolloutGroupId
|
||||
* the ID rolloutgroup the actions belong to
|
||||
* @param status
|
||||
* the status the actions should have
|
||||
* @return the count of actions referring to a rollout, rolloutgroup and are
|
||||
* in a given status
|
||||
*/
|
||||
Long countByRolloutIdAndRolloutGroupIdAndStatus(Long rolloutId, Long rolloutGroupId, Action.Status status);
|
||||
|
||||
/**
|
||||
* Retrieving all actions referring to a given rollout with a specific
|
||||
* action as parent reference and a specific status.
|
||||
*
|
||||
* Finding all actions of a specific rolloutgroup parent relation.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the actions belong to
|
||||
* @param rolloutGroupParent
|
||||
* the parent rolloutgroup the actions should reference
|
||||
* @param actionStatus
|
||||
* the status the actions have
|
||||
* @return the actions referring a specific rollout and a specific parent
|
||||
* rolloutgroup in a specific status
|
||||
*/
|
||||
List<Action> findByRolloutAndRolloutGroupParentAndStatus(Rollout rollout, RolloutGroup rolloutGroupParent,
|
||||
Status actionStatus);
|
||||
|
||||
/**
|
||||
* Retrieves all actions for a specific rollout and in a specific status.
|
||||
*
|
||||
* @param rollout
|
||||
* 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> findByRolloutAndStatus(Rollout rollout, Status actionStatus);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in
|
||||
* each status in specified rollout.
|
||||
*
|
||||
* @param rolloutId
|
||||
* id of {@link Rollout}
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus( a.rollout.id, a.status , COUNT(a.target)) FROM Action a WHERE a.rollout.id IN ?1 GROUP BY a.rollout.id,a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutId(List<Long> rolloutId);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in
|
||||
* each status in specified rollout.
|
||||
*
|
||||
* @param rolloutId
|
||||
* id of {@link Rollout}
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus( a.rollout.id, a.status , COUNT(a.target)) FROM Action a WHERE a.rollout.id = ?1 GROUP BY a.rollout.id,a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutId(Long rolloutId);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in
|
||||
* each status in specified rollout group.
|
||||
*
|
||||
* @param rolloutGroupId
|
||||
* id of {@link RolloutGroup}
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status , COUNT(a.target)) FROM Action a WHERE a.rolloutGroup.id = ?1 GROUP BY a.rolloutGroup.id, a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutGroupId(Long rolloutGroupId);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in
|
||||
* each status in specified rollout group.
|
||||
*
|
||||
* @param rolloutGroupId
|
||||
* list of id of {@link RolloutGroup}
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status , COUNT(a.target)) FROM Action a WHERE a.rolloutGroup.id IN ?1 GROUP BY a.rolloutGroup.id, a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutGroupId(List<Long> rolloutGroupId);
|
||||
|
||||
// Asha-ends here
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.ListJoin;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@@ -46,6 +45,9 @@ import org.eclipse.hawkbit.repository.model.Action_;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout_;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -228,7 +230,7 @@ public class DeploymentManagement {
|
||||
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
|
||||
}
|
||||
|
||||
return assignDistributionSetToTargets(set, targets);
|
||||
return assignDistributionSetToTargets(set, targets, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,8 +239,45 @@ public class DeploymentManagement {
|
||||
*
|
||||
* @param dsID
|
||||
* the ID of the distribution set to assign
|
||||
* @param targetsWithActionType
|
||||
* @param targets
|
||||
* a list of all targets and their action type
|
||||
* @param rollout
|
||||
* the rollout for this assignment
|
||||
* @param rolloutgroup
|
||||
* the rolloutgroup for this assignment
|
||||
* @return the assignment result
|
||||
*
|
||||
* @throw IncompleteDistributionSetException if mandatory
|
||||
* {@link SoftwareModuleType} are not assigned as define by the
|
||||
* {@link DistributionSetType}.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
|
||||
public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID,
|
||||
final List<TargetWithActionType> targets, final Rollout rollout, final RolloutGroup rolloutGroup) {
|
||||
final DistributionSet set = distributoinSetRepository.findOne(dsID);
|
||||
if (set == null) {
|
||||
throw new EntityNotFoundException(
|
||||
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
|
||||
}
|
||||
|
||||
return assignDistributionSetToTargets(set, targets, rollout, rolloutGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* method assigns the {@link DistributionSet} to all {@link Target}s by
|
||||
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
||||
*
|
||||
* @param dsID
|
||||
* the ID of the distribution set to assign
|
||||
* @param targets
|
||||
* a list of all targets and their action type
|
||||
* @param rollout
|
||||
* the rollout for this assignment
|
||||
* @param rolloutgroup
|
||||
* the rolloutgroup for this assignment
|
||||
* @return the assignment result
|
||||
*
|
||||
* @throw IncompleteDistributionSetException if mandatory
|
||||
@@ -246,7 +285,8 @@ public class DeploymentManagement {
|
||||
* {@link DistributionSetType}.
|
||||
*/
|
||||
private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final DistributionSet set,
|
||||
final List<TargetWithActionType> targetsWithActionType) {
|
||||
final List<TargetWithActionType> targetsWithActionType, final Rollout rollout,
|
||||
final RolloutGroup rolloutGroup) {
|
||||
|
||||
if (!set.isComplete()) {
|
||||
throw new IncompleteDistributionSetException(
|
||||
@@ -293,8 +333,10 @@ public class DeploymentManagement {
|
||||
final Set<Long> targetIdsCancellList = new HashSet<Long>();
|
||||
targetIds.forEach(ids -> targetIdsCancellList.addAll(overrideObsoleteUpdateActions(ids)));
|
||||
|
||||
// check for old assigments that needs cancelation
|
||||
// final List<Long> canncelledTargetIds =
|
||||
// cancel all scheduled actions which are in-active, these actions were
|
||||
// not active before and the manual assignment which has been done
|
||||
// cancels the
|
||||
targetIds.forEach(tIds -> actionRepository.switchStatus(Status.CANCELED, tIds, false, Status.SCHEDULED));
|
||||
|
||||
// set assigned distribution set and TargetUpdateStatus
|
||||
final String currentUser;
|
||||
@@ -317,6 +359,8 @@ public class DeploymentManagement {
|
||||
tAction.setStatus(Status.RUNNING);
|
||||
tAction.setTarget(t);
|
||||
tAction.setDistributionSet(set);
|
||||
tAction.setRollout(rollout);
|
||||
tAction.setRolloutGroup(rolloutGroup);
|
||||
return tAction;
|
||||
}).collect(Collectors.toList())).stream()
|
||||
.collect(Collectors.toMap(a -> a.getTarget().getControllerId(), Function.identity()));
|
||||
@@ -359,7 +403,7 @@ public class DeploymentManagement {
|
||||
/**
|
||||
* Sends the {@link TargetAssignDistributionSetEvent} for a specific target
|
||||
* to the {@link EventBus}.
|
||||
*
|
||||
*
|
||||
* @param target
|
||||
* the Target which has been assigned to a distribution set
|
||||
* @param actionId
|
||||
@@ -372,14 +416,14 @@ public class DeploymentManagement {
|
||||
target.getTargetInfo().setUpdateStatus(TargetUpdateStatus.PENDING);
|
||||
afterCommit.afterCommit(() -> {
|
||||
eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo()));
|
||||
eventBus.post(new TargetAssignDistributionSetEvent(target.getControllerId(), actionId, softwareModules,
|
||||
target.getTargetInfo().getAddress()));
|
||||
eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(),
|
||||
target.getControllerId(), actionId, softwareModules, target.getTargetInfo().getAddress()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@link UpdateAction}s that are no longer necessary and sends
|
||||
* cancellations to the controller.
|
||||
* cancelations to the controller.
|
||||
*
|
||||
* @param myTarget
|
||||
* to override {@link UpdateAction}s
|
||||
@@ -390,13 +434,14 @@ public class DeploymentManagement {
|
||||
|
||||
// Figure out if there are potential target/action combinations that
|
||||
// need to be considered
|
||||
// for cancellation
|
||||
// for cancelation
|
||||
final List<Action> activeActions = actionRepository
|
||||
.findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(targetsIds,
|
||||
Action.Status.CANCELING);
|
||||
activeActions.forEach(action -> {
|
||||
action.setStatus(Status.CANCELING);
|
||||
// document that the status has been retrieved
|
||||
|
||||
actionStatusRepository.save(new ActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
|
||||
"manual cancelation requested"));
|
||||
|
||||
@@ -404,15 +449,20 @@ public class DeploymentManagement {
|
||||
|
||||
cancelledTargetIds.add(action.getTarget().getId());
|
||||
});
|
||||
|
||||
actionRepository.save(activeActions);
|
||||
|
||||
return cancelledTargetIds;
|
||||
|
||||
}
|
||||
|
||||
private DistributionSetAssignmentResult assignDistributionSetByTargetId(@NotNull final DistributionSet set,
|
||||
@NotEmpty final List<String> tIDs, final ActionType actionType, final long forcedTime) {
|
||||
|
||||
return assignDistributionSetToTargets(set, tIDs.stream()
|
||||
.map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()));
|
||||
.map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()), null,
|
||||
null);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -475,7 +525,6 @@ public class DeploymentManagement {
|
||||
actionStatusRepository.save(new ActionStatus(myAction, Status.CANCELING, System.currentTimeMillis(),
|
||||
"manual cancelation requested"));
|
||||
final Action saveAction = actionRepository.save(myAction);
|
||||
|
||||
cancelAssignDistributionSetEvent(target, myAction.getId());
|
||||
|
||||
return saveAction;
|
||||
@@ -488,15 +537,15 @@ public class DeploymentManagement {
|
||||
/**
|
||||
* Sends the {@link CancelTargetAssignmentEvent} for a specific target to
|
||||
* the {@link EventBus}.
|
||||
*
|
||||
*
|
||||
* @param target
|
||||
* the Target which has been assigned to a distribution set
|
||||
* @param actionId
|
||||
* the action id of the assignment
|
||||
*/
|
||||
private void cancelAssignDistributionSetEvent(final Target target, final Long actionId) {
|
||||
afterCommit.afterCommit(() -> eventBus.post(new CancelTargetAssignmentEvent(target.getControllerId(), actionId,
|
||||
target.getTargetInfo().getAddress())));
|
||||
afterCommit.afterCommit(() -> eventBus.post(new CancelTargetAssignmentEvent(target.getOptLockRevision(),
|
||||
target.getTenant(), target.getControllerId(), actionId, target.getTargetInfo().getAddress())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -542,6 +591,111 @@ public class DeploymentManagement {
|
||||
return actionRepository.save(mergedAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an action entry into the action repository. In case of existing
|
||||
* scheduled actions the scheduled actions gets canceled. A scheduled action
|
||||
* is created in-active.
|
||||
*
|
||||
* @param targets
|
||||
* the targets to create scheduled actions for
|
||||
* @param distributionSet
|
||||
* the distribution set for the actions
|
||||
* @param actionType
|
||||
* the action type for the action
|
||||
* @param forcedTime
|
||||
* the forcedTime of the action
|
||||
* @param rollout
|
||||
* the rollout for this action
|
||||
* @param rolloutGroup
|
||||
* the rolloutgroup for this action
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public void createScheduledAction(final List<Target> targets, final DistributionSet distributionSet,
|
||||
final ActionType actionType, final long forcedTime, final Rollout rollout,
|
||||
final RolloutGroup rolloutGroup) {
|
||||
// cancel all current scheduled actions for this target. E.g. an action
|
||||
// is already scheduled and a next action is created then cancel the
|
||||
// current scheduled action to cancel. E.g. a new scheduled action is
|
||||
// created.
|
||||
final List<Long> targetIds = targets.stream().map(t -> t.getId()).collect(Collectors.toList());
|
||||
actionRepository.switchStatus(Action.Status.CANCELED, targetIds, false, Action.Status.SCHEDULED);
|
||||
targets.forEach(target -> {
|
||||
final Action action = new Action();
|
||||
action.setTarget(target);
|
||||
action.setActive(false);
|
||||
action.setDistributionSet(distributionSet);
|
||||
action.setActionType(actionType);
|
||||
action.setForcedTime(forcedTime);
|
||||
action.setStatus(Status.SCHEDULED);
|
||||
action.setRollout(rollout);
|
||||
action.setRolloutGroup(rolloutGroup);
|
||||
actionRepository.save(action);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting an action which is scheduled, e.g. in case of rollout a
|
||||
* scheduled action must be started now.
|
||||
*
|
||||
* @param action
|
||||
* the action to start now.
|
||||
* @return the action which has been started
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
public Action startScheduledAction(@NotNull final Action action) {
|
||||
|
||||
final Action mergedAction = entityManager.merge(action);
|
||||
final Target mergedTarget = entityManager.merge(action.getTarget());
|
||||
|
||||
// check if we need to override running update actions
|
||||
final Set<Long> overrideObsoleteUpdateActions = overrideObsoleteUpdateActions(
|
||||
Collections.singletonList(action.getTarget().getId()));
|
||||
|
||||
final boolean hasDistributionSetAlreadyAssigned = targetRepository
|
||||
.count(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(
|
||||
Collections.singletonList(mergedTarget.getControllerId()),
|
||||
action.getDistributionSet().getId())) == 0;
|
||||
if (hasDistributionSetAlreadyAssigned) {
|
||||
// the target has already the distribution set assigned, we don't
|
||||
// need to start the scheduled action, just finished it.
|
||||
mergedAction.setStatus(Status.FINISHED);
|
||||
mergedAction.setActive(false);
|
||||
return actionRepository.save(mergedAction);
|
||||
}
|
||||
|
||||
mergedAction.setActive(true);
|
||||
mergedAction.setStatus(Status.RUNNING);
|
||||
final Action savedAction = actionRepository.save(mergedAction);
|
||||
|
||||
final ActionStatus actionStatus = new ActionStatus();
|
||||
actionStatus.setAction(action);
|
||||
actionStatus.setOccurredAt(action.getCreatedAt());
|
||||
actionStatus.setStatus(Status.RUNNING);
|
||||
actionStatusRepository.save(actionStatus);
|
||||
|
||||
mergedTarget.setAssignedDistributionSet(action.getDistributionSet());
|
||||
final TargetInfo targetInfo = mergedTarget.getTargetInfo();
|
||||
targetInfo.setUpdateStatus(TargetUpdateStatus.PENDING);
|
||||
targetRepository.save(mergedTarget);
|
||||
targetInfoRepository.save(targetInfo);
|
||||
|
||||
// in case we canceled an action before for this target, then don't fire
|
||||
// assignment event
|
||||
if (!overrideObsoleteUpdateActions.contains(savedAction.getId())) {
|
||||
final List<SoftwareModule> softwareModules = softwareModuleRepository
|
||||
.findByAssignedTo(action.getDistributionSet());
|
||||
// send distribution set assignment event
|
||||
|
||||
assignDistributionSetEvent(mergedAction.getTarget(), mergedAction.getId(), softwareModules);
|
||||
}
|
||||
return savedAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Action} entity for given actionId.
|
||||
*
|
||||
@@ -609,13 +763,14 @@ public class DeploymentManagement {
|
||||
final Root<Action> actionRoot = query.from(Action.class);
|
||||
final ListJoin<Action, ActionStatus> actionStatusJoin = actionRoot.join(Action_.actionStatus, JoinType.LEFT);
|
||||
final Join<Action, DistributionSet> actionDsJoin = actionRoot.join(Action_.distributionSet);
|
||||
final Join<Action, Rollout> actionRolloutJoin = actionRoot.join(Action_.rollout, JoinType.LEFT);
|
||||
|
||||
final CriteriaQuery<ActionWithStatusCount> multiselect = query.distinct(true).multiselect(
|
||||
actionRoot.get(Action_.id), actionRoot.get(Action_.actionType), actionRoot.get(Action_.active),
|
||||
actionRoot.get(Action_.forcedTime), actionRoot.get(Action_.status), actionRoot.get(Action_.createdAt),
|
||||
actionRoot.get(Action_.lastModifiedAt), actionDsJoin.get(DistributionSet_.id),
|
||||
actionDsJoin.get(DistributionSet_.name), actionDsJoin.get(DistributionSet_.version),
|
||||
cb.count(actionStatusJoin));
|
||||
cb.count(actionStatusJoin), actionRolloutJoin.get(Rollout_.name));
|
||||
multiselect.where(cb.equal(actionRoot.get(Action_.target), target));
|
||||
multiselect.orderBy(cb.desc(actionRoot.get(Action_.id)));
|
||||
multiselect.groupBy(actionRoot.get(Action_.id));
|
||||
@@ -640,25 +795,19 @@ public class DeploymentManagement {
|
||||
public Slice<Action> findActionsByTarget(final Specification<Action> specifiction, final Target target,
|
||||
final Pageable pageable) {
|
||||
|
||||
return actionRepository.findAll(new Specification<Action>() {
|
||||
|
||||
@Override
|
||||
public Predicate toPredicate(final Root<Action> root, final CriteriaQuery<?> query,
|
||||
final CriteriaBuilder cb) {
|
||||
return cb.and(specifiction.toPredicate(root, query, cb), cb.equal(root.get(Action_.target), target));
|
||||
}
|
||||
}, pageable);
|
||||
return actionRepository.findAll((Specification<Action>) (root, query, cb) -> cb
|
||||
.and(specifiction.toPredicate(root, query, cb), cb.equal(root.get(Action_.target), target)), pageable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Action}s which are referring the given
|
||||
* {@link Target}.
|
||||
*
|
||||
* @param pageable
|
||||
* page parameters
|
||||
*
|
||||
* @param foundTarget
|
||||
* the target to find assigned actions
|
||||
* @return the found {@link Action}s
|
||||
* the target to find actions for
|
||||
* @param pageable
|
||||
* the pageable request to limit, sort the actions
|
||||
* @return a slice of actions found for a specific target
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
public Slice<Action> findActionsByTarget(final Target foundTarget, final Pageable pageable) {
|
||||
@@ -744,13 +893,8 @@ public class DeploymentManagement {
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
public Long countActionsByTarget(@NotNull final Specification<Action> spec, @NotNull final Target target) {
|
||||
return actionRepository.count(new Specification<Action>() {
|
||||
@Override
|
||||
public Predicate toPredicate(final Root<Action> root, final CriteriaQuery<?> query,
|
||||
final CriteriaBuilder cb) {
|
||||
return cb.and(spec.toPredicate(root, query, cb), cb.equal(root.get(Action_.target), target));
|
||||
}
|
||||
});
|
||||
return actionRepository.count((root, query, cb) -> cb.and(spec.toPredicate(root, query, cb),
|
||||
cb.equal(root.get(Action_.target), target)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -802,7 +946,7 @@ public class DeploymentManagement {
|
||||
* This method is called, when cancellation has been successful. It sets the
|
||||
* action to canceled, resets the meta data of the target and in case there
|
||||
* is a new action this action is triggered.
|
||||
*
|
||||
*
|
||||
* @param action
|
||||
* the action which is set to canceled
|
||||
*/
|
||||
@@ -824,4 +968,40 @@ public class DeploymentManagement {
|
||||
}
|
||||
targetManagement.updateTarget(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieving all actions referring to a given rollout with a specific
|
||||
* action as parent reference and a specific status.
|
||||
*
|
||||
* Finding all actions of a specific rolloutgroup parent relation.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the actions belong to
|
||||
* @param rolloutGroupParent
|
||||
* the parent rolloutgroup the actions should reference
|
||||
* @param actionStatus
|
||||
* the status the actions have
|
||||
* @return the actions referring a specific rollout and a specific parent
|
||||
* rolloutgroup in a specific status
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
public List<Action> findActionsByRolloutGroupParentAndStatus(final Rollout rollout,
|
||||
final RolloutGroup rolloutGroupParent, final Action.Status actionStatus) {
|
||||
return actionRepository.findByRolloutAndRolloutGroupParentAndStatus(rollout, rolloutGroupParent, actionStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all actions for a specific rollout and in a specific status.
|
||||
*
|
||||
* @param rollout
|
||||
* 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
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
public List<Action> findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) {
|
||||
return actionRepository.findByRolloutAndStatus(rollout, actionStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
/**
|
||||
* An implementation of the {@link PageRequest} which is offset based by means
|
||||
* the offset is given and not the page number as in the original
|
||||
* {@link PageRequest} implemntation where the offset is generated. Due that the
|
||||
* REST-API is working with {@code offset} and {@code limit} parameter we need
|
||||
* an offset based page request for JPA.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
* @since 0.2.2
|
||||
*
|
||||
*/
|
||||
public final class OffsetBasedPageRequest extends PageRequest {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final int offset;
|
||||
|
||||
/**
|
||||
* Creates a new {@link OffsetBasedPageRequest}. Offsets are zero indexed,
|
||||
* thus providing 0 for {@code offset} will return the first entry.
|
||||
*
|
||||
* @param offset
|
||||
* zero-based offset index.
|
||||
* @param limit
|
||||
* the limit of the page to be returned.
|
||||
*/
|
||||
public OffsetBasedPageRequest(final int offset, final int limit) {
|
||||
this(offset, limit, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link OffsetBasedPageRequest}. Offsets are zero indexed,
|
||||
* thus providing 0 for {@code offset} will return the first entry.
|
||||
*
|
||||
* @param offset
|
||||
* zero-based offset index.
|
||||
* @param limit
|
||||
* the limit of the page to be returned.
|
||||
* @param sort
|
||||
* sort can be {@literal null}.
|
||||
*/
|
||||
public OffsetBasedPageRequest(final int offset, final int limit, final Sort sort) {
|
||||
super(0, limit, sort);
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OffsetBasedPageRequest [offset=" + offset + ", getPageSize()=" + getPageSize() + ", getPageNumber()="
|
||||
+ getPageNumber() + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* 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.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.ListJoin;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action_;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup_;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutTargetGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutTargetGroup_;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Target_;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
||||
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.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* RolloutGroupManagement to control rollout groups. This service secures all
|
||||
* the functionality based on the {@link PreAuthorize} annotation on methods.
|
||||
*/
|
||||
@Validated
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
public class RolloutGroupManagement {
|
||||
|
||||
@Autowired
|
||||
private RolloutGroupRepository rolloutGroupRepository;
|
||||
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetRepository targetRepository;
|
||||
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
|
||||
/**
|
||||
* Retrieves a single {@link RolloutGroup} by its ID.
|
||||
*
|
||||
* @param rolloutGroupId
|
||||
* the ID of the rollout group to find
|
||||
* @return the found {@link RolloutGroup} by its ID or {@code null} if it
|
||||
* does not exists
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public RolloutGroup findRolloutGroupById(final Long rolloutGroupId) {
|
||||
return rolloutGroupRepository.findOne(rolloutGroupId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a page of {@link RolloutGroup}s filtered by a given
|
||||
* {@link Rollout}.
|
||||
*
|
||||
* @param rolloutId
|
||||
* the ID of the rollout to filter the {@link RolloutGroup}s
|
||||
* @param page
|
||||
* the page request to sort and limit the result
|
||||
* @return a page of found {@link RolloutGroup}s
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Page<RolloutGroup> findRolloutGroupsByRolloutId(final Long rolloutId, final Pageable page) {
|
||||
return rolloutGroupRepository.findByRolloutId(rolloutId, page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a page of {@link RolloutGroup}s filtered by a given
|
||||
* {@link Rollout} and the given {@link Specification}.
|
||||
*
|
||||
* @param rolloutId
|
||||
* the ID of the rollout to filter the {@link RolloutGroup}s
|
||||
* @param specification
|
||||
* the specification to filter the result set based on attributes
|
||||
* of the {@link RolloutGroup}
|
||||
* @param page
|
||||
* the page request to sort and limit the result
|
||||
* @return a page of found {@link RolloutGroup}s
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Page<RolloutGroup> findRolloutGroupsByPredicate(final Rollout rollout,
|
||||
final Specification<RolloutGroup> specification, final Pageable page) {
|
||||
return rolloutGroupRepository.findAll((root, query, criteriaBuilder) -> criteriaBuilder.and(
|
||||
criteriaBuilder.equal(root.get(RolloutGroup_.rollout), rollout),
|
||||
specification.toPredicate(root, query, criteriaBuilder)), page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a page of {@link RolloutGroup}s filtered by a given
|
||||
* {@link Rollout} with the detailed status.
|
||||
*
|
||||
* @param rolloutId
|
||||
* the ID of the rollout to filter the {@link RolloutGroup}s
|
||||
* @param page
|
||||
* the page request to sort and limit the result
|
||||
* @return a page of found {@link RolloutGroup}s
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(final Long rolloutId, final Pageable page) {
|
||||
final Page<RolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutId(rolloutId, page);
|
||||
final List<Long> rolloutGroupIds = rolloutGroups.getContent().stream().map(rollout -> rollout.getId())
|
||||
.collect(Collectors.toList());
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRolloutGroup(
|
||||
rolloutGroupIds);
|
||||
|
||||
for (final RolloutGroup rolloutGroup : rolloutGroups) {
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
||||
allStatesForRollout.get(rolloutGroup.getId()), rolloutGroup.getTotalTargets());
|
||||
rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
}
|
||||
|
||||
return rolloutGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of targets in different status in rollout group.
|
||||
*
|
||||
* @param rolloutGroupId
|
||||
* rollout group id
|
||||
* @return rolloutGroup with details of targets count for different statuses
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public RolloutGroup findRolloutGroupWithDetailedStatus(final Long rolloutGroupId) {
|
||||
final RolloutGroup rolloutGroup = findRolloutGroupById(rolloutGroupId);
|
||||
final List<TotalTargetCountActionStatus> rolloutStatusCountItems = actionRepository
|
||||
.getStatusCountByRolloutGroupId(rolloutGroupId);
|
||||
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
|
||||
rolloutGroup.getTotalTargets());
|
||||
rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
return rolloutGroup;
|
||||
|
||||
}
|
||||
|
||||
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRolloutGroup(
|
||||
final List<Long> rolloutGroupIds) {
|
||||
final List<TotalTargetCountActionStatus> resultList = actionRepository
|
||||
.getStatusCountByRolloutGroupId(rolloutGroupIds);
|
||||
return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get targets of specified rollout group.
|
||||
*
|
||||
* @param rolloutGroup
|
||||
* rollout group
|
||||
* @param specification
|
||||
* the specification for filtering the targets of a rollout group
|
||||
* @param page
|
||||
* the page request to sort and limit the result
|
||||
*
|
||||
* @return Page<Target> list of targets of a rollout group
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Page<Target> findRolloutGroupTargets(final RolloutGroup rolloutGroup,
|
||||
final Specification<Target> specification, final Pageable page) {
|
||||
return targetRepository.findAll((root, query, criteriaBuilder) -> {
|
||||
final ListJoin<Target, RolloutTargetGroup> rolloutTargetJoin = root.join(Target_.rolloutTargetGroup);
|
||||
return criteriaBuilder.and(specification.toPredicate(root, query, criteriaBuilder),
|
||||
criteriaBuilder.equal(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup));
|
||||
} , page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get targets of specified rollout group.
|
||||
*
|
||||
* @param rolloutGroup
|
||||
* rollout group
|
||||
* @param page
|
||||
* the page request to sort and limit the result
|
||||
*
|
||||
* @return Page<Target> list of targets of a rollout group
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Page<Target> findRolloutGroupTargets(@NotNull final RolloutGroup rolloutGroup, final Pageable page) {
|
||||
if (isRolloutStatusReady(rolloutGroup)) {
|
||||
// in case of status ready the action has not been created yet and
|
||||
// the relation information between target and rollout-group is
|
||||
// stored in the #TargetRolloutGroup.
|
||||
return targetRepository.findByRolloutTargetGroupRolloutGroupId(rolloutGroup.getId(), page);
|
||||
}
|
||||
return targetRepository.findByActionsRolloutGroup(rolloutGroup, page);
|
||||
}
|
||||
|
||||
private boolean isRolloutStatusReady(final RolloutGroup rolloutGroup) {
|
||||
return rolloutGroup != null && RolloutStatus.READY.equals(rolloutGroup.getRollout().getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Find all targets with action status by rollout group id. The action
|
||||
* status might be {@code null} if for the target within the rollout no
|
||||
* actions as been created, e.g. the target already had assigned the same
|
||||
* distribution set we do not create an action for it but the target is in
|
||||
* the result list of the rollout-group.
|
||||
*
|
||||
* @param pageRequest
|
||||
* the page request to sort and limit the result
|
||||
* @param rolloutGroup
|
||||
* rollout group
|
||||
* @return {@link TargetWithActionStatus} target with action status
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Page<TargetWithActionStatus> findAllTargetsWithActionStatus(final PageRequest pageRequest,
|
||||
@NotNull final RolloutGroup rolloutGroup) {
|
||||
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
|
||||
final CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
|
||||
|
||||
final Root<RolloutTargetGroup> targetRoot = query.distinct(true).from(RolloutTargetGroup.class);
|
||||
final Join<RolloutTargetGroup, Target> targetJoin = targetRoot.join(RolloutTargetGroup_.target);
|
||||
final ListJoin<RolloutTargetGroup, Action> actionJoin = targetRoot.join(RolloutTargetGroup_.actions,
|
||||
JoinType.LEFT);
|
||||
|
||||
final Root<RolloutTargetGroup> countQueryFrom = countQuery.distinct(true).from(RolloutTargetGroup.class);
|
||||
countQuery
|
||||
.select(cb.count(countQueryFrom.join(RolloutTargetGroup_.target).join(Target_.actions, JoinType.LEFT)))
|
||||
.where(cb.equal(countQueryFrom.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup));
|
||||
final Long totalCount = entityManager.createQuery(countQuery).getSingleResult();
|
||||
|
||||
final CriteriaQuery<Object[]> multiselect = query.multiselect(targetJoin, actionJoin.get(Action_.status))
|
||||
.where(cb.equal(targetRoot.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup));
|
||||
final List<TargetWithActionStatus> targetWithActionStatus = entityManager.createQuery(multiselect)
|
||||
.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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* 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.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* The repository interface for the {@link RolloutGroup} model.
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public interface RolloutGroupRepository
|
||||
extends BaseEntityRepository<RolloutGroup, Long>, JpaSpecificationExecutor<RolloutGroup> {
|
||||
|
||||
/**
|
||||
* Retrieves all {@link RolloutGroup} referring a specific rollout in the
|
||||
* order of creating them. ID ASC.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroups belong to
|
||||
* @return the rollout groups belonging to a rollout ordered by ID ASC.
|
||||
*/
|
||||
List<RolloutGroup> findByRolloutOrderByIdAsc(final Rollout rollout);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link RolloutGroup} referring a specific rollout in a
|
||||
* specific {@link RolloutGroupStatus}.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroup belong to
|
||||
* @param status
|
||||
* the status of the rollout groups
|
||||
* @return the rollout groups belonging to a rollout in a specific status
|
||||
*/
|
||||
List<RolloutGroup> findByRolloutAndStatus(final Rollout rollout, final RolloutGroupStatus status);
|
||||
|
||||
/**
|
||||
* Counts all {@link RolloutGroup} referring a specific rollout.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroup belong to
|
||||
* @return the count of the rollout groups for a specific rollout
|
||||
*/
|
||||
Long countByRollout(final Rollout rollout);
|
||||
|
||||
/**
|
||||
* Counts all {@link RolloutGroup} referring a specific rollout in a
|
||||
* specific {@link RolloutGroupStatus}.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroup belong to
|
||||
* @param rolloutGroupStatus
|
||||
* the status of the rollout groups
|
||||
* @return the count of rollout groups belonging to a rollout in a specific
|
||||
* status
|
||||
*/
|
||||
Long countByRolloutAndStatus(Rollout rollout, RolloutGroupStatus rolloutGroupStatus);
|
||||
|
||||
/**
|
||||
* Counts all {@link RolloutGroup} referring a specific rollout in specific
|
||||
* {@link RolloutGroupStatus}s. An in-clause statement does not work with
|
||||
* the spring-data, so this is specific usecase regarding to the
|
||||
* rollout-management to find out rolloutgroups which are in specific
|
||||
* states.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroup belong to
|
||||
* @param rolloutGroupStatus1
|
||||
* the status of the rollout groups
|
||||
* @param rolloutGroupStatus2
|
||||
* the status of the rollout groups
|
||||
* @return the count of rollout groups belonging to a rollout in specific
|
||||
* status
|
||||
*/
|
||||
@Query("SELECT COUNT(r.id) FROM RolloutGroup r WHERE r.rollout = :rollout and (r.status = :status1 or r.status = :status2)")
|
||||
Long countByRolloutAndStatusOrStatus(@Param("rollout") Rollout rollout,
|
||||
@Param("status1") RolloutGroupStatus rolloutGroupStatus1,
|
||||
@Param("status2") RolloutGroupStatus rolloutGroupStatus2);
|
||||
|
||||
/**
|
||||
* 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 status
|
||||
* the status of the rolloutgroups
|
||||
* @return The child {@link RolloutGroup}s in a specific status
|
||||
*/
|
||||
List<RolloutGroup> findByParentAndStatus(RolloutGroup rolloutGroup, RolloutGroupStatus status);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link RolloutGroup} for a specific rollout and status not
|
||||
* having ordered by ID DESC, latest top.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroup belong to
|
||||
* @param notStatus
|
||||
* the status which the rolloutgroup should not have
|
||||
* @return rolloutgroup referring to a rollout and not having a specific
|
||||
* status ordered by ID DESC.
|
||||
*/
|
||||
List<RolloutGroup> findByRolloutAndStatusNotOrderByIdDesc(Rollout rollout, RolloutGroupStatus notStatus);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link RolloutGroup} for a specific rollout.
|
||||
*
|
||||
* @param rolloutId
|
||||
* the ID of the rollout to find the rollout groups
|
||||
* @param page
|
||||
* the page request to sort, limit the result
|
||||
* @return a page of found {@link RolloutGroup} or {@code empty}.
|
||||
*/
|
||||
Page<RolloutGroup> findByRolloutId(final Long rolloutId, Pageable page);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,870 @@
|
||||
/**
|
||||
* 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.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.cache.CacheWriteNotify;
|
||||
import org.eclipse.hawkbit.eventbus.event.RolloutGroupCreatedEvent;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
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.RolloutGroup.RolloutGroupConditions;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutTargetGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout_;
|
||||
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.rollout.condition.RolloutGroupActionEvaluator;
|
||||
import org.eclipse.hawkbit.rollout.condition.RolloutGroupConditionEvaluator;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* RolloutManagement to control rollouts e.g. like creating, starting, resuming
|
||||
* and pausing rollouts. This service secures all the functionality based on the
|
||||
* {@link PreAuthorize} annotation on methods.
|
||||
*/
|
||||
@Validated
|
||||
@Service
|
||||
@EnableScheduling
|
||||
@Transactional(readOnly = true)
|
||||
public class RolloutManagement {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class);
|
||||
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Autowired
|
||||
private RolloutRepository rolloutRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetManagement targetManagement;
|
||||
|
||||
@Autowired
|
||||
private TargetRepository targetRepository;
|
||||
|
||||
@Autowired
|
||||
private RolloutGroupRepository rolloutGroupRepository;
|
||||
|
||||
@Autowired
|
||||
private DeploymentManagement deploymentManagement;
|
||||
|
||||
@Autowired
|
||||
private RolloutTargetGroupRepository rolloutTargetGroupRepository;
|
||||
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private NoCountPagingRepository criteriaNoCountDao;
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager txManager;
|
||||
|
||||
@Autowired
|
||||
private CacheWriteNotify cacheWriteNotify;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("asyncExecutor")
|
||||
private Executor executor;
|
||||
|
||||
/*
|
||||
* set which stores the rollouts which are asynchronously creating. This is
|
||||
* necessary to verify rollouts which maybe stuck during creationg e.g.
|
||||
* because of database interruption, failures or even application crash.
|
||||
* !This is not cluster aware!
|
||||
*/
|
||||
private static final Set<String> creatingRollouts = ConcurrentHashMap.newKeySet();
|
||||
|
||||
/*
|
||||
* set which stores the rollouts which are asynchronously starting. This is
|
||||
* necessary to verify rollouts which maybe stuck during starting e.g.
|
||||
* because of database interruption, failures or even application crash.
|
||||
* !This is not cluster aware!
|
||||
*/
|
||||
private static final Set<String> startingRollouts = ConcurrentHashMap.newKeySet();
|
||||
|
||||
/**
|
||||
* Retrieves all rollouts.
|
||||
*
|
||||
* @param page
|
||||
* the page request to sort and limit the result
|
||||
* @return a page of found rollouts
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Page<Rollout> findAll(final Pageable page) {
|
||||
return rolloutRepository.findAll(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all rollouts found by the given specification.
|
||||
*
|
||||
* @param specification
|
||||
* the specification to filter rollouts
|
||||
* @param page
|
||||
* the page request to sort and limit the result
|
||||
* @return a page of found rollouts
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Page<Rollout> findAllWithDetailedStatusByPredicate(final Specification<Rollout> specification,
|
||||
final Pageable page) {
|
||||
final Page<Rollout> findAll = rolloutRepository.findAll(specification, page);
|
||||
setRolloutStatusDetails(findAll);
|
||||
return findAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific rollout by its ID.
|
||||
*
|
||||
* @param rolloutId
|
||||
* the ID of the rollout to retrieve
|
||||
* @return the founded rollout or {@code null} if rollout with given ID does
|
||||
* not exists
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Rollout findRolloutById(final Long rolloutId) {
|
||||
return rolloutRepository.findOne(rolloutId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists a new rollout entity. The filter within the
|
||||
* {@link Rollout#getTargetFilterQuery()} is used to retrieve the targets
|
||||
* which are effected by this rollout to create. The targets will then be
|
||||
* split up into groups. The size of the groups can be defined in the
|
||||
* {@code groupSize} parameter.
|
||||
*
|
||||
* The rollout is not started. Only the preparation of the rollout is done,
|
||||
* persisting and creating all the necessary groups. The Rollout and the
|
||||
* groups are persisted in {@link RolloutStatus#READY} and
|
||||
* {@link RolloutGroupStatus#READY} so they can be started
|
||||
* {@link #startRollout(Rollout)}.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout entity to create
|
||||
* @param amountGroup
|
||||
* the amount of groups to split the rollout into
|
||||
* @param conditions
|
||||
* the rolloutgroup conditions and actions which should be
|
||||
* applied for each {@link RolloutGroup}
|
||||
* @return the persisted rollout.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* in case the given groupSize is zero or lower.
|
||||
*/
|
||||
@Transactional
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||
public Rollout createRollout(final Rollout rollout, final int amountGroup,
|
||||
final RolloutGroupConditions conditions) {
|
||||
final Rollout savedRollout = createRollout(rollout, amountGroup);
|
||||
return createRolloutGroups(amountGroup, conditions, savedRollout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists a new rollout entity. The filter within the
|
||||
* {@link Rollout#getTargetFilterQuery()} is used to retrieve the targets
|
||||
* which are effected by this rollout to create. The creation of the rollout
|
||||
* will be done synchronously and will be returned. The targets will then be
|
||||
* split up into groups. The size of the groups can be defined in the
|
||||
* {@code groupSize} parameter.
|
||||
*
|
||||
* The creation of the rollout groups is executed asynchronously due it
|
||||
* might take some time to split up the targets into groups. The creation of
|
||||
* the {@link RolloutGroup} is published as event
|
||||
* {@link RolloutGroupCreatedEvent}.
|
||||
*
|
||||
* The rollout is in status {@link RolloutStatus#CREATING} until all rollout
|
||||
* groups has been created and the targets are split up, then the rollout
|
||||
* will change the status to {@link RolloutStatus#READY}.
|
||||
*
|
||||
* The rollout is not started. Only the preparation of the rollout is done,
|
||||
* persisting and creating all the necessary groups. The Rollout and the
|
||||
* groups are persisted in {@link RolloutStatus#READY} and
|
||||
* {@link RolloutGroupStatus#READY} so they can be started
|
||||
* {@link #startRollout(Rollout)}.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout to be created
|
||||
* @param amountGroup
|
||||
* the number of groups should be created for the rollout and
|
||||
* split up the targets
|
||||
* @param conditions
|
||||
* the rolloutgroup conditions and actions which should be
|
||||
* applied for each {@link RolloutGroup}
|
||||
* @return the created rollout entity in state
|
||||
* {@link RolloutStatus#CREATING}
|
||||
*/
|
||||
@Transactional
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||
public Rollout createRolloutAsync(final Rollout rollout, final int amountGroup,
|
||||
final RolloutGroupConditions conditions) {
|
||||
final Rollout savedRollout = createRollout(rollout, amountGroup);
|
||||
creatingRollouts.add(savedRollout.getName());
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
|
||||
def.setName("creatingRollout");
|
||||
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
new TransactionTemplate(txManager, def).execute(status -> {
|
||||
createRolloutGroups(amountGroup, conditions, savedRollout);
|
||||
return null;
|
||||
});
|
||||
} finally {
|
||||
creatingRollouts.remove(savedRollout.getName());
|
||||
}
|
||||
});
|
||||
return savedRollout;
|
||||
}
|
||||
|
||||
private Rollout createRollout(final Rollout rollout, final int amountGroup) {
|
||||
verifyRolloutGroupParameter(amountGroup);
|
||||
final Long totalTargets = targetManagement.countTargetByTargetFilterQuery(rollout.getTargetFilterQuery());
|
||||
rollout.setTotalTargets(totalTargets.longValue());
|
||||
return rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
private void verifyRolloutGroupParameter(final int amountGroup) {
|
||||
if (amountGroup <= 0) {
|
||||
throw new IllegalArgumentException("the amountGroup must be greater than zero");
|
||||
} else if (amountGroup > 500) {
|
||||
throw new IllegalArgumentException("the amountGroup must not be greater than 500");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for creating rollout groups and calculating group sizes. Group
|
||||
* sizes are calculated by dividing the total count of targets through the
|
||||
* amount of given groups. In same cases this will lead to less rollout
|
||||
* groups than given by client.
|
||||
*
|
||||
* @param amountGroup
|
||||
* the amount of groups
|
||||
* @param conditions
|
||||
* the rollout group conditions
|
||||
* @param savedRollout
|
||||
* the rollout
|
||||
* @return the rollout with created groups
|
||||
*/
|
||||
private Rollout createRolloutGroups(final int amountGroup, final RolloutGroupConditions conditions,
|
||||
final Rollout savedRollout) {
|
||||
int pageIndex = 0;
|
||||
int groupIndex = 0;
|
||||
final Long totalCount = savedRollout.getTotalTargets();
|
||||
final int groupSize = (int) Math.ceil((double) totalCount / (double) amountGroup);
|
||||
// validate if the amount of groups that will be created are the amount
|
||||
// of groups that the client what's to have created.
|
||||
int amountGroupValidated = amountGroup;
|
||||
final int amountGroupCreation = (int) (Math.ceil((double) totalCount / (double) groupSize));
|
||||
if (amountGroupCreation == (amountGroup - 1)) {
|
||||
amountGroupValidated--;
|
||||
}
|
||||
RolloutGroup lastSavedGroup = null;
|
||||
while (pageIndex < totalCount) {
|
||||
groupIndex++;
|
||||
final String nameAndDesc = "group-" + groupIndex;
|
||||
final RolloutGroup group = new RolloutGroup();
|
||||
group.setName(nameAndDesc);
|
||||
group.setDescription(nameAndDesc);
|
||||
group.setRollout(savedRollout);
|
||||
group.setParent(lastSavedGroup);
|
||||
group.setSuccessCondition(conditions.getSuccessCondition());
|
||||
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
|
||||
group.setErrorCondition(conditions.getErrorCondition());
|
||||
group.setErrorConditionExp(conditions.getErrorConditionExp());
|
||||
group.setErrorAction(conditions.getErrorAction());
|
||||
group.setErrorActionExp(conditions.getErrorActionExp());
|
||||
|
||||
final RolloutGroup savedGroup = rolloutGroupRepository.save(group);
|
||||
|
||||
final Slice<Target> targetGroup = targetManagement.findTargetsAll(savedRollout.getTargetFilterQuery(),
|
||||
new OffsetBasedPageRequest(pageIndex, groupSize, new Sort(Direction.ASC, "id")));
|
||||
savedGroup.setTotalTargets(targetGroup.getContent().size());
|
||||
|
||||
lastSavedGroup = savedGroup;
|
||||
|
||||
targetGroup
|
||||
.forEach(target -> rolloutTargetGroupRepository.save(new RolloutTargetGroup(savedGroup, target)));
|
||||
cacheWriteNotify.rolloutGroupCreated(groupIndex, savedRollout.getId(), savedGroup.getId(),
|
||||
amountGroupValidated, groupIndex);
|
||||
pageIndex += groupSize;
|
||||
|
||||
}
|
||||
|
||||
savedRollout.setStatus(RolloutStatus.READY);
|
||||
return rolloutRepository.save(savedRollout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a rollout which has been created. The rollout must be in
|
||||
* {@link RolloutStatus#READY} state. The according actions will be created
|
||||
* for each affected target in the rollout. The actions of the first group
|
||||
* will be started immediately {@link RolloutGroupStatus#RUNNING} as the
|
||||
* other groups will be {@link RolloutGroupStatus#SCHEDULED} state.
|
||||
*
|
||||
* The rollout itself will be then also in {@link RolloutStatus#RUNNING}.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout to be started
|
||||
*
|
||||
* @throws RolloutIllegalStateException
|
||||
* if given rollout is not in {@link RolloutStatus#READY}. Only
|
||||
* ready rollouts can be started.
|
||||
*/
|
||||
@Transactional
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
public Rollout startRollout(final Rollout rollout) {
|
||||
final Rollout mergedRollout = entityManager.merge(rollout);
|
||||
checkIfRolloutCanStarted(rollout, mergedRollout);
|
||||
return doStartRollout(mergedRollout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a rollout asynchronously which has been created. The rollout must
|
||||
* be in {@link RolloutStatus#READY} state. The according actions will be
|
||||
* created asynchronously for each affected target in the rollout. The
|
||||
* actions of the first group will be started immediately
|
||||
* {@link RolloutGroupStatus#RUNNING} as the other groups will be
|
||||
* {@link RolloutGroupStatus#SCHEDULED} state.
|
||||
*
|
||||
* The rollout itself will be then also in {@link RolloutStatus#RUNNING}.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout to be started
|
||||
*
|
||||
* @throws RolloutIllegalStateException
|
||||
* if given rollout is not in {@link RolloutStatus#READY}. Only
|
||||
* ready rollouts can be started.
|
||||
*/
|
||||
@Transactional
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
public Rollout startRolloutAsync(final Rollout rollout) {
|
||||
final Rollout mergedRollout = entityManager.merge(rollout);
|
||||
checkIfRolloutCanStarted(rollout, mergedRollout);
|
||||
mergedRollout.setStatus(RolloutStatus.STARTING);
|
||||
final Rollout updatedRollout = rolloutRepository.save(mergedRollout);
|
||||
startingRollouts.add(updatedRollout.getName());
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
|
||||
def.setName("startingRollout");
|
||||
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
new TransactionTemplate(txManager, def).execute(status -> {
|
||||
doStartRollout(updatedRollout);
|
||||
return null;
|
||||
});
|
||||
} finally {
|
||||
startingRollouts.remove(updatedRollout.getName());
|
||||
}
|
||||
});
|
||||
return updatedRollout;
|
||||
|
||||
}
|
||||
|
||||
private Rollout doStartRollout(final Rollout rollout) {
|
||||
final DistributionSet distributionSet = rollout.getDistributionSet();
|
||||
final ActionType actionType = rollout.getActionType();
|
||||
final long forceTime = rollout.getForcedTime();
|
||||
final List<RolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutOrderByIdAsc(rollout);
|
||||
for (int iGroup = 0; iGroup < rolloutGroups.size(); iGroup++) {
|
||||
final RolloutGroup rolloutGroup = rolloutGroups.get(iGroup);
|
||||
final List<Target> targetGroup = targetRepository.findByRolloutTargetGroupRolloutGroup(rolloutGroup);
|
||||
// firstgroup can already be started
|
||||
if (iGroup == 0) {
|
||||
final List<TargetWithActionType> targetsWithActionType = targetGroup.stream()
|
||||
.map(t -> new TargetWithActionType(t.getControllerId(), actionType, forceTime))
|
||||
.collect(Collectors.toList());
|
||||
deploymentManagement.assignDistributionSet(distributionSet.getId(), targetsWithActionType, rollout,
|
||||
rolloutGroup);
|
||||
rolloutGroup.setStatus(RolloutGroupStatus.RUNNING);
|
||||
} else {
|
||||
// create only not active actions with status scheduled so they
|
||||
// can be activated later
|
||||
deploymentManagement.createScheduledAction(targetGroup, distributionSet, actionType, forceTime, rollout,
|
||||
rolloutGroup);
|
||||
rolloutGroup.setStatus(RolloutGroupStatus.SCHEDULED);
|
||||
}
|
||||
rolloutGroupRepository.save(rolloutGroup);
|
||||
}
|
||||
rollout.setStatus(RolloutStatus.RUNNING);
|
||||
return rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses a rollout which is currently running. The Rollout switches
|
||||
* {@link RolloutStatus#PAUSED}. {@link RolloutGroup}s which are currently
|
||||
* running will be untouched. {@link RolloutGroup}s which are
|
||||
* {@link RolloutGroupStatus#SCHEDULED} will not be started and keep in
|
||||
* {@link RolloutGroupStatus#SCHEDULED} state until the rollout is
|
||||
* {@link RolloutManagement#resumeRollout(Rollout)}.
|
||||
*
|
||||
* Switching the rollout status to {@link RolloutStatus#PAUSED} is
|
||||
* sufficient due the {@link #checkRunningRollouts(long)} will not check
|
||||
* this rollout anymore.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout to be paused.
|
||||
*
|
||||
* @throws RolloutIllegalStateException
|
||||
* if given rollout is not in {@link RolloutStatus#RUNNING}.
|
||||
* Only running rollouts can be paused.
|
||||
*/
|
||||
@Transactional
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
public void pauseRollout(final Rollout rollout) {
|
||||
final Rollout mergedRollout = entityManager.merge(rollout);
|
||||
if (mergedRollout.getStatus() != RolloutStatus.RUNNING) {
|
||||
throw new RolloutIllegalStateException("Rollout can only be paused in state running but current state is "
|
||||
+ rollout.getStatus().name().toLowerCase());
|
||||
}
|
||||
// setting the complete rollout only in paused state. This is sufficient
|
||||
// due the currently running groups will be completed and new groups are
|
||||
// not started until rollout goes back to running state again. The
|
||||
// periodically check for running rollouts will skip rollouts in pause
|
||||
// state.
|
||||
mergedRollout.setStatus(RolloutStatus.PAUSED);
|
||||
rolloutRepository.save(mergedRollout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes a paused rollout. The rollout switches back to
|
||||
* {@link RolloutStatus#RUNNING} state which is then picked up again by the
|
||||
* {@link #checkRunningRollouts(long)}.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout to be resumed
|
||||
* @throws RolloutIllegalStateException
|
||||
* if given rollout is not in {@link RolloutStatus#PAUSED}. Only
|
||||
* paused rollouts can be resumed.
|
||||
*/
|
||||
@Transactional
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
public void resumeRollout(final Rollout rollout) {
|
||||
final Rollout mergedRollout = entityManager.merge(rollout);
|
||||
if (!(RolloutStatus.PAUSED.equals(mergedRollout.getStatus()))) {
|
||||
throw new RolloutIllegalStateException("Rollout can only be resumed in state paused but current state is "
|
||||
+ rollout.getStatus().name().toLowerCase());
|
||||
}
|
||||
mergedRollout.setStatus(RolloutStatus.RUNNING);
|
||||
rolloutRepository.save(mergedRollout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checking running rollouts. Rollouts which are checked updating the
|
||||
* {@link Rollout#setLastCheck(long)} 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 attend to be called by a scheduler.
|
||||
* {@link RolloutScheduler}. 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).
|
||||
*/
|
||||
@Transactional
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
public void checkRunningRollouts(final long delayBetweenChecks) {
|
||||
verifyStuckedRollouts();
|
||||
final long lastCheck = System.currentTimeMillis();
|
||||
final int updated = rolloutRepository.updateLastCheck(lastCheck, delayBetweenChecks, RolloutStatus.RUNNING);
|
||||
|
||||
if (updated == 0) {
|
||||
// nothing to check, maybe another instance already checked in
|
||||
// between
|
||||
LOGGER.info("No rolloutcheck necessary for current scheduled check {}, next check at {}", lastCheck,
|
||||
lastCheck + delayBetweenChecks);
|
||||
return;
|
||||
}
|
||||
|
||||
final List<Rollout> rolloutsToCheck = rolloutRepository.findByLastCheckAndStatus(lastCheck,
|
||||
RolloutStatus.RUNNING);
|
||||
LOGGER.info("Found {} running rollouts to check", rolloutsToCheck.size());
|
||||
|
||||
for (final Rollout rollout : rolloutsToCheck) {
|
||||
LOGGER.debug("Checking rollout {}", rollout);
|
||||
final List<RolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutAndStatus(rollout,
|
||||
RolloutGroupStatus.RUNNING);
|
||||
|
||||
if (rolloutGroups.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(), rolloutGroups.size());
|
||||
executeRolloutGroups(rollout, rolloutGroups);
|
||||
}
|
||||
|
||||
if (isRolloutComplete(rollout)) {
|
||||
LOGGER.info("Rollout {} is finished, setting finished status", rollout);
|
||||
rollout.setStatus(RolloutStatus.FINISHED);
|
||||
rolloutRepository.save(rollout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies and handles stucked rollouts in asynchronous creation or
|
||||
* starting state. If rollouts are created or started asynchronously it
|
||||
* might be that they keep in state {@link RolloutStatus#CREATING} or
|
||||
* {@link RolloutStatus#STARTING} due database or application interruption.
|
||||
* In case this happens, set the rollout to error state.
|
||||
*/
|
||||
private void verifyStuckedRollouts() {
|
||||
final List<Rollout> rolloutsInCreatingState = rolloutRepository.findByStatus(RolloutStatus.CREATING);
|
||||
rolloutsInCreatingState.stream().filter(rollout -> !creatingRollouts.contains(rollout.getName()))
|
||||
.forEach(rollout -> {
|
||||
LOGGER.warn(
|
||||
"Determined error during rollout creation of rollout {}, stucking in creating state, setting to status",
|
||||
rollout, RolloutStatus.ERROR_CREATING);
|
||||
rollout.setStatus(RolloutStatus.ERROR_CREATING);
|
||||
rolloutRepository.save(rollout);
|
||||
});
|
||||
|
||||
final List<Rollout> rolloutsInStartingState = rolloutRepository.findByStatus(RolloutStatus.STARTING);
|
||||
rolloutsInStartingState.stream().filter(rollout -> !startingRollouts.contains(rollout.getName()))
|
||||
.forEach(rollout -> {
|
||||
LOGGER.warn(
|
||||
"Determined error during rollout starting of rollout {}, stucking in starting state, setting to status",
|
||||
rollout, RolloutStatus.ERROR_STARTING);
|
||||
rollout.setStatus(RolloutStatus.ERROR_STARTING);
|
||||
rolloutRepository.save(rollout);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void executeRolloutGroups(final Rollout rollout, final List<RolloutGroup> rolloutGroups) {
|
||||
for (final RolloutGroup rolloutGroup : rolloutGroups) {
|
||||
// error state check, do we need to stop the whole
|
||||
// rollout because of error?
|
||||
final RolloutGroupErrorCondition errorCondition = rolloutGroup.getErrorCondition();
|
||||
final boolean isError = checkErrorState(rollout, rolloutGroup, errorCondition);
|
||||
if (isError) {
|
||||
LOGGER.info("Rollout {} {} has error, calling error action", rollout.getName(), rollout.getId());
|
||||
callErrorAction(rollout, rolloutGroup);
|
||||
} else {
|
||||
// not in error so check finished state, do we need to
|
||||
// start the next group?
|
||||
final RolloutGroupSuccessCondition finishedCondition = rolloutGroup.getSuccessCondition();
|
||||
checkFinishCondition(rollout, rolloutGroup, finishedCondition);
|
||||
if (isRolloutGroupComplete(rollout, rolloutGroup)) {
|
||||
rolloutGroup.setStatus(RolloutGroupStatus.FINISHED);
|
||||
rolloutGroupRepository.save(rolloutGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void executeLatestRolloutGroup(final Rollout rollout) {
|
||||
final List<RolloutGroup> latestRolloutGroup = rolloutGroupRepository
|
||||
.findByRolloutAndStatusNotOrderByIdDesc(rollout, RolloutGroupStatus.SCHEDULED);
|
||||
if (latestRolloutGroup.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
executeRolloutGroupSuccessAction(rollout, latestRolloutGroup.get(0));
|
||||
}
|
||||
|
||||
private void callErrorAction(final Rollout rollout, final RolloutGroup rolloutGroup) {
|
||||
try {
|
||||
context.getBean(rolloutGroup.getErrorAction().getBeanName(), RolloutGroupActionEvaluator.class)
|
||||
.eval(rollout, rolloutGroup, rolloutGroup.getErrorActionExp());
|
||||
} catch (final BeansException e) {
|
||||
LOGGER.error("Something bad happend when accessing the error action bean {}",
|
||||
rolloutGroup.getErrorAction().getBeanName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRolloutComplete(final Rollout rollout) {
|
||||
final Long groupsActiveLeft = rolloutGroupRepository.countByRolloutAndStatusOrStatus(rollout,
|
||||
RolloutGroupStatus.RUNNING, RolloutGroupStatus.SCHEDULED);
|
||||
return groupsActiveLeft == 0;
|
||||
}
|
||||
|
||||
private boolean isRolloutGroupComplete(final Rollout rollout, final RolloutGroup rolloutGroup) {
|
||||
final Long actionsLeftForRollout = actionRepository
|
||||
.countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(rollout, rolloutGroup,
|
||||
Action.Status.ERROR, Action.Status.FINISHED, Action.Status.CANCELED);
|
||||
return actionsLeftForRollout == 0;
|
||||
}
|
||||
|
||||
private boolean checkErrorState(final Rollout rollout, final RolloutGroup rolloutGroup,
|
||||
final RolloutGroupErrorCondition errorCondition) {
|
||||
if (errorCondition == null) {
|
||||
// there is no error condition, so return false, don't have error.
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return context.getBean(errorCondition.getBeanName(), RolloutGroupConditionEvaluator.class).eval(rollout,
|
||||
rolloutGroup, rolloutGroup.getErrorConditionExp());
|
||||
} catch (final BeansException e) {
|
||||
LOGGER.error("Something bad happend when accessing the error condition bean {}",
|
||||
errorCondition.getBeanName(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkFinishCondition(final Rollout rollout, final RolloutGroup rolloutGroup,
|
||||
final RolloutGroupSuccessCondition finishCondition) {
|
||||
LOGGER.trace("Checking finish condition {} on rolloutgroup {}", finishCondition, rolloutGroup);
|
||||
try {
|
||||
final boolean isFinished = context
|
||||
.getBean(finishCondition.getBeanName(), RolloutGroupConditionEvaluator.class)
|
||||
.eval(rollout, rolloutGroup, rolloutGroup.getSuccessConditionExp());
|
||||
if (isFinished) {
|
||||
LOGGER.info("Rolloutgroup {} is finished, starting next group", rolloutGroup);
|
||||
executeRolloutGroupSuccessAction(rollout, rolloutGroup);
|
||||
} else {
|
||||
LOGGER.debug("Rolloutgroup {} is still running", rolloutGroup);
|
||||
}
|
||||
return isFinished;
|
||||
} catch (final BeansException e) {
|
||||
LOGGER.error("Something bad happend when accessing the finish condition bean {}",
|
||||
finishCondition.getBeanName(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void executeRolloutGroupSuccessAction(final Rollout rollout, final RolloutGroup rolloutGroup) {
|
||||
context.getBean(rolloutGroup.getSuccessAction().getBeanName(), RolloutGroupActionEvaluator.class).eval(rollout,
|
||||
rolloutGroup, rolloutGroup.getSuccessActionExp());
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts all {@link Target}s in the repository.
|
||||
*
|
||||
* @return number of targets
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Long countRolloutsAll() {
|
||||
return rolloutRepository.count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count rollouts by specified filter text.
|
||||
*
|
||||
* @param searchText
|
||||
* name or description
|
||||
* @return total count rollouts for specified filter text.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Long countRolloutsAllByFilters(final String searchText) {
|
||||
return rolloutRepository.count(likeNameOrDescription(searchText));
|
||||
}
|
||||
|
||||
private static Specification<Rollout> likeNameOrDescription(final String searchText) {
|
||||
return (rolloutRoot, query, criteriaBuilder) -> {
|
||||
final String searchTextToLower = searchText.toLowerCase();
|
||||
return criteriaBuilder.or(
|
||||
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(Rollout_.name)), searchTextToLower),
|
||||
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(Rollout_.description)),
|
||||
searchTextToLower));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* * Retrieves a specific rollout by its ID.
|
||||
*
|
||||
* @param pageable
|
||||
* the page request to sort and limit the result
|
||||
* @param searchText
|
||||
* search text which matches name or description of rollout
|
||||
* @return the founded rollout or {@code null} if rollout with given ID does
|
||||
* not exists
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Slice<Rollout> findRolloutByFilters(final Pageable pageable, @NotEmpty final String searchText) {
|
||||
final Specification<Rollout> specs = likeNameOrDescription(searchText);
|
||||
final Slice<Rollout> findAll = criteriaNoCountDao.findAll(specs, pageable, Rollout.class);
|
||||
setRolloutStatusDetails(findAll);
|
||||
return findAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific rollout by its name.
|
||||
*
|
||||
* @param rolloutName
|
||||
* the name of the rollout to retrieve
|
||||
* @return the founded rollout or {@code null} if rollout with given name
|
||||
* does not exists
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Rollout findRolloutByName(final String rolloutName) {
|
||||
return rolloutRepository.findByName(rolloutName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update rollout details.
|
||||
*
|
||||
* @param rollout
|
||||
* rollout to be updated
|
||||
*
|
||||
* @return Rollout updated rollout
|
||||
*/
|
||||
@NotNull
|
||||
@Transactional
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||
public Rollout updateRollout(@NotNull final Rollout rollout) {
|
||||
Assert.notNull(rollout.getId());
|
||||
return rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of targets in different status in rollout.
|
||||
*
|
||||
* @param page
|
||||
* the page request to sort and limit the result
|
||||
* @return a list of rollouts with details of targets count for different
|
||||
* statuses
|
||||
*
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Page<Rollout> findAllRolloutsWithDetailedStatus(final Pageable page) {
|
||||
final Page<Rollout> rollouts = findAll(page);
|
||||
setRolloutStatusDetails(rollouts);
|
||||
return rollouts;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of targets in different status in rollout.
|
||||
*
|
||||
* @param rolloutId
|
||||
* rollout id
|
||||
* @return rollout details of targets count for different statuses
|
||||
*
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
public Rollout findRolloutWithDetailedStatus(final Long rolloutId) {
|
||||
final Rollout rollout = findRolloutById(rolloutId);
|
||||
final List<TotalTargetCountActionStatus> rolloutStatusCountItems = actionRepository
|
||||
.getStatusCountByRolloutId(rolloutId);
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
|
||||
rollout.getTotalTargets());
|
||||
rollout.setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
return rollout;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
private void setRolloutStatusDetails(final Slice<Rollout> rollouts) {
|
||||
final List<Long> rolloutIds = rollouts.getContent().stream().map(rollout -> rollout.getId())
|
||||
.collect(Collectors.toList());
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRollout(
|
||||
rolloutIds);
|
||||
|
||||
for (final Rollout rollout : rollouts) {
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
||||
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets());
|
||||
rollout.setTotalTargetCountStatus(totalTargetCountStatus);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIfRolloutCanStarted(final Rollout rollout, final Rollout mergedRollout) {
|
||||
if (!(RolloutStatus.READY.equals(mergedRollout.getStatus()))) {
|
||||
throw new RolloutIllegalStateException("Rollout can only be started in state ready but current state is "
|
||||
+ rollout.getStatus().name().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* Get finished percentage details for a specified group which is in running
|
||||
* state.
|
||||
*
|
||||
* @param rolloutId
|
||||
* the ID of the {@link Rollout}
|
||||
* @param rolloutGroup
|
||||
* the ID of the {@link RolloutGroup}
|
||||
* @return percentage finished
|
||||
*/
|
||||
public float getFinishedPercentForRunningGroup(final Long rolloutId, final RolloutGroup rolloutGroup) {
|
||||
final Long totalGroup = rolloutGroup.getTotalTargets();
|
||||
final Long finished = actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rolloutId,
|
||||
rolloutGroup.getId(), Action.Status.FINISHED);
|
||||
if (totalGroup == 0) {
|
||||
// in case e.g. targets has been deleted we don't have any actions
|
||||
// left for this group, so the group is finished
|
||||
return 100;
|
||||
}
|
||||
// calculate threshold
|
||||
return ((float) finished / (float) totalGroup) * 100;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* 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.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
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.Transactional;
|
||||
|
||||
/**
|
||||
* The repository interface for the {@link Rollout} model.
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public interface RolloutRepository extends BaseEntityRepository<Rollout, Long>, JpaSpecificationExecutor<Rollout> {
|
||||
|
||||
/**
|
||||
* Updates the {@code lastCheck} field of the {@link Rollout} for rollouts
|
||||
* in a specific status and only if the {@code lastCheck} is overdue.
|
||||
*
|
||||
* @param lastCheck
|
||||
* the time in milliseconds to set to the lastCheck column
|
||||
* @param delay
|
||||
* the delay between last checks
|
||||
* @param status
|
||||
* 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
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("UPDATE Rollout 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<Rollout> findByLastCheckAndStatus(long lastCheck, RolloutStatus status);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Rollout} for a specific {@code name}
|
||||
*
|
||||
* @param name
|
||||
* the rollout name
|
||||
* @return {@link Rollout} for specific name
|
||||
*/
|
||||
Page<Rollout> findByName(final Pageable pageable, String name);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Rollout} for a specific {@code name}
|
||||
*
|
||||
* @param name
|
||||
* the rollout name
|
||||
* @return {@link Rollout} for specific name
|
||||
*/
|
||||
Rollout findByName(String name);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Rollout} for a specific status.
|
||||
*
|
||||
* @param status
|
||||
* the status of the rollouts to retrieve
|
||||
* @return a list of {@link Rollout} having the given status
|
||||
*/
|
||||
List<Rollout> findByStatus(final RolloutStatus status);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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.List;
|
||||
|
||||
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.EnvironmentAware;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Scheduler to schedule the
|
||||
* {@link RolloutManagement#checkRunningRollouts(long)}. The delay between the
|
||||
* checks be be configured using the property
|
||||
* {@link #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 implements EnvironmentAware {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RolloutScheduler.class);
|
||||
|
||||
private static final String PROP_SCHEDULER_DELAY = "hawkbit.rollout.scheduler.fixedDelay";
|
||||
private static final long DEFAULT_SCHEDULER_DELAY = 30000L;
|
||||
private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${" + PROP_SCHEDULER_DELAY + ":"
|
||||
+ DEFAULT_SCHEDULER_DELAY + "}";
|
||||
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
|
||||
@Autowired
|
||||
private SystemManagement systemManagement;
|
||||
|
||||
@Autowired
|
||||
private RolloutManagement rolloutManagement;
|
||||
|
||||
@Autowired
|
||||
private SystemSecurityContext systemSecurityContext;
|
||||
|
||||
private long fixedDelay = DEFAULT_SCHEDULER_DELAY;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@link SystemSecurityContext}.
|
||||
*/
|
||||
@Scheduled(initialDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER)
|
||||
public void rolloutScheduler() {
|
||||
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(() -> {
|
||||
// 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 rollouts for {} tenants", tenants.size());
|
||||
for (final String tenant : tenants) {
|
||||
tenantAware.runAsTenant(tenant, () -> {
|
||||
rolloutManagement.checkRunningRollouts(fixedDelay);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(final Environment environment) {
|
||||
fixedDelay = environment.getProperty(PROP_SCHEDULER_DELAY, Long.class, DEFAULT_SCHEDULER_DELAY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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.eclipse.hawkbit.repository.model.RolloutTargetGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutTargetGroupId;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
public interface RolloutTargetGroupRepository
|
||||
extends CrudRepository<RolloutTargetGroup, RolloutTargetGroupId>, JpaSpecificationExecutor<RolloutTargetGroup> {
|
||||
}
|
||||
@@ -96,6 +96,12 @@ public class SystemManagement implements EnvironmentAware {
|
||||
@Autowired
|
||||
private TenantConfigurationRepository tenantConfigurationRepository;
|
||||
|
||||
@Autowired
|
||||
private RolloutRepository rolloutRepository;
|
||||
|
||||
@Autowired
|
||||
private RolloutGroupRepository rolloutGroupRepository;
|
||||
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
|
||||
@@ -209,7 +215,8 @@ public class SystemManagement implements EnvironmentAware {
|
||||
* @return list of all tenant names in the system.
|
||||
*/
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
// tenant independent
|
||||
public List<String> findTenants() {
|
||||
return tenantMetaDataRepository.findAll().stream().map(md -> md.getTenant()).collect(Collectors.toList());
|
||||
@@ -234,11 +241,13 @@ public class SystemManagement implements EnvironmentAware {
|
||||
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
|
||||
tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant);
|
||||
targetRepository.deleteByTenantIgnoreCase(tenant);
|
||||
actionRepository.deleteByTenantIgnoreCase(tenant);
|
||||
rolloutGroupRepository.deleteByTenantIgnoreCase(tenant);
|
||||
rolloutRepository.deleteByTenantIgnoreCase(tenant);
|
||||
artifactRepository.deleteByTenantIgnoreCase(tenant);
|
||||
externalArtifactRepository.deleteByTenantIgnoreCase(tenant);
|
||||
externalArtifactProviderRepository.deleteByTenantIgnoreCase(tenant);
|
||||
targetTagRepository.deleteByTenantIgnoreCase(tenant);
|
||||
actionRepository.deleteByTenantIgnoreCase(tenant);
|
||||
distributionSetTagRepository.deleteByTenantIgnoreCase(tenant);
|
||||
distributionSetRepository.deleteByTenantIgnoreCase(tenant);
|
||||
distributionSetTypeRepository.deleteByTenantIgnoreCase(tenant);
|
||||
|
||||
@@ -12,10 +12,12 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionStatus;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -278,4 +280,43 @@ public interface TargetRepository extends BaseEntityRepository<Target, Long>, Jp
|
||||
@Query("UPDATE Target t SET t.assignedDistributionSet = :set, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy WHERE t.id IN :targets")
|
||||
void setAssignedDistributionSet(@Param("set") DistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
|
||||
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
|
||||
|
||||
List<Target> findByRolloutTargetGroupRolloutGroup(final RolloutGroup rolloutGroup);
|
||||
|
||||
/**
|
||||
*
|
||||
* Finds all targets of a rollout group.
|
||||
*
|
||||
* @param rolloutGroupId
|
||||
* the ID of the rollout group
|
||||
* @param page
|
||||
* the page request parameter
|
||||
* @return a page of all targets related to a rollout group
|
||||
*/
|
||||
Page<Target> findByRolloutTargetGroupRolloutGroupId(final Long rolloutGroupId, Pageable page);
|
||||
|
||||
/**
|
||||
* Finds all targets related to a target rollout group stored for a specific
|
||||
* rollout.
|
||||
*
|
||||
* @param rolloutGroup
|
||||
* the rollout group the targets should belong to
|
||||
* @param page
|
||||
* the page request parameter
|
||||
* @return a page of all targets related to a rollout group
|
||||
*/
|
||||
Page<Target> findByActionsRolloutGroup(RolloutGroup rolloutGroup, Pageable page);
|
||||
|
||||
/**
|
||||
* Find all targets with action status for a specific group.
|
||||
*
|
||||
* @param pageable
|
||||
* the page request parameter
|
||||
* @param rolloutGroupId
|
||||
* the ID of the rollout group
|
||||
* @return targets with action status
|
||||
*/
|
||||
@Query("select DISTINCT NEW org.eclipse.hawkbit.repository.model.TargetWithActionStatus(a.target,a.status) from Action a inner join fetch a.target t where a.rolloutGroup.id = :rolloutGroupId")
|
||||
Page<TargetWithActionStatus> findTargetsWithActionStatusByRolloutGroupId(final Pageable pageable,
|
||||
@Param("rolloutGroupId") Long rolloutGroupId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.exception;
|
||||
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
import org.eclipse.hawkbit.exception.SpServerRtException;
|
||||
|
||||
/**
|
||||
* the {@link RolloutIllegalStateException} is thrown when a rollout is changing
|
||||
* it's state which is not valid. E.g. trying to start a already running
|
||||
* rollout, or trying to resume a already finished rollout.
|
||||
*
|
||||
*/
|
||||
public class RolloutIllegalStateException extends SpServerRtException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final SpServerError THIS_ERROR = SpServerError.SP_ROLLOUT_ILLEGAL_STATE;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public RolloutIllegalStateException() {
|
||||
super(THIS_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameterized constructor.
|
||||
*
|
||||
* @param cause
|
||||
* of the exception
|
||||
*/
|
||||
public RolloutIllegalStateException(final Throwable cause) {
|
||||
super(THIS_ERROR, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameterized constructor.
|
||||
*
|
||||
* @param message
|
||||
* of the exception
|
||||
* @param cause
|
||||
* of the exception
|
||||
*/
|
||||
public RolloutIllegalStateException(final String message, final Throwable cause) {
|
||||
super(message, THIS_ERROR, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameterized constructor.
|
||||
*
|
||||
* @param message
|
||||
* of the exception
|
||||
*/
|
||||
public RolloutIllegalStateException(final String message) {
|
||||
super(message, THIS_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -94,6 +94,14 @@ public class Action extends BaseEntity implements Comparable<Action> {
|
||||
CascadeType.REMOVE })
|
||||
private List<ActionStatus> actionStatus;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "rolloutgroup", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rolloutgroup") )
|
||||
private RolloutGroup rolloutGroup;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "rollout", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rollout") )
|
||||
private Rollout rollout;
|
||||
|
||||
/**
|
||||
* Note: filled only in {@link Status#DOWNLOAD}.
|
||||
*/
|
||||
@@ -221,6 +229,36 @@ public class Action extends BaseEntity implements Comparable<Action> {
|
||||
this.forcedTime = forcedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rolloutGroup
|
||||
*/
|
||||
public RolloutGroup getRolloutGroup() {
|
||||
return rolloutGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rolloutGroup
|
||||
* the rolloutGroup to set
|
||||
*/
|
||||
public void setRolloutGroup(final RolloutGroup rolloutGroup) {
|
||||
this.rolloutGroup = rolloutGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rollout
|
||||
*/
|
||||
public Rollout getRollout() {
|
||||
return rollout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rollout
|
||||
* the rollout to set
|
||||
*/
|
||||
public void setRollout(final Rollout rollout) {
|
||||
this.rollout = rollout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final Action o) {
|
||||
if (super.getId() == null || o == null || o.getId() == null) {
|
||||
@@ -379,7 +417,13 @@ public class Action extends BaseEntity implements Comparable<Action> {
|
||||
/**
|
||||
* Action needs download by this target which has now started.
|
||||
*/
|
||||
DOWNLOAD;
|
||||
DOWNLOAD,
|
||||
|
||||
/**
|
||||
* Action is in waiting state, e.g. the action is scheduled in a rollout
|
||||
* but not yet activated.
|
||||
*/
|
||||
SCHEDULED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,6 +32,7 @@ public class ActionWithStatusCount {
|
||||
private final String dsName;
|
||||
private final String dsVersion;
|
||||
private final Action action;
|
||||
private final String rolloutName;
|
||||
|
||||
/**
|
||||
* JPA constructor, the parameter are the result set columns of the custom
|
||||
@@ -59,10 +60,14 @@ public class ActionWithStatusCount {
|
||||
* the version of the distributionset
|
||||
* @param actionStatusCount
|
||||
* the count of the action status for this action
|
||||
* @param rolloutName
|
||||
* the rollout name
|
||||
*/
|
||||
|
||||
public ActionWithStatusCount(final Long actionId, final ActionType actionType, final boolean active,
|
||||
final long forcedTime, final Status status, final Long actionCreatedAt, final Long actionLastModifiedAt,
|
||||
final Long dsId, final String dsName, final String dsVersion, final Long actionStatusCount) {
|
||||
final Long dsId, final String dsName, final String dsVersion, final Long actionStatusCount,
|
||||
final String rolloutName) {
|
||||
this.actionId = actionId;
|
||||
this.actionType = actionType;
|
||||
this.actionActive = active;
|
||||
@@ -74,6 +79,7 @@ public class ActionWithStatusCount {
|
||||
this.dsName = dsName;
|
||||
this.dsVersion = dsVersion;
|
||||
this.actionStatusCount = actionStatusCount;
|
||||
this.rolloutName = rolloutName;
|
||||
|
||||
this.action = new Action();
|
||||
this.action.setActionType(actionType);
|
||||
@@ -166,4 +172,12 @@ public class ActionWithStatusCount {
|
||||
public Long getActionStatusCount() {
|
||||
return actionStatusCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rolloutName
|
||||
*/
|
||||
public String getRolloutName() {
|
||||
return rolloutName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import javax.persistence.PrePersist;
|
||||
import javax.persistence.Version;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.CacheFieldEntityListener;
|
||||
import org.eclipse.hawkbit.eventbus.EntityPropertyChangeListener;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.TenantAwareHolder;
|
||||
@@ -45,7 +46,7 @@ import org.springframework.hateoas.Identifiable;
|
||||
*/
|
||||
@MappedSuperclass
|
||||
@Access(AccessType.FIELD)
|
||||
@EntityListeners({ AuditingEntityListener.class, CacheFieldEntityListener.class })
|
||||
@EntityListeners({ AuditingEntityListener.class, CacheFieldEntityListener.class, EntityPropertyChangeListener.class })
|
||||
@TenantDiscriminatorColumn(name = "tenant", length = 40)
|
||||
@Multitenant(MultitenantType.SINGLE_TABLE)
|
||||
public abstract class BaseEntity implements Serializable, Identifiable<Long> {
|
||||
@@ -98,9 +99,9 @@ public abstract class BaseEntity implements Serializable, Identifiable<Long> {
|
||||
// service
|
||||
final String currentTenant = SystemManagementHolder.getInstance().currentTenant();
|
||||
if (currentTenant == null) {
|
||||
throw new TenantNotExistException(
|
||||
"Tenant " + TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant()
|
||||
+ " does not exists, cannot create entity " + this.getClass() + " with id " + id);
|
||||
throw new TenantNotExistException("Tenant "
|
||||
+ TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant()
|
||||
+ " does not exists, cannot create entity " + this.getClass() + " with id " + id);
|
||||
}
|
||||
setTenant(currentTenant.toUpperCase());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
/**
|
||||
* 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.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.cache.CacheField;
|
||||
import org.eclipse.hawkbit.cache.CacheKeys;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
|
||||
/**
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_rollout", indexes = {
|
||||
@Index(name = "sp_idx_rollout_01", columnList = "tenant,name") }, uniqueConstraints = @UniqueConstraint(columnNames = {
|
||||
"name", "tenant" }, name = "uk_rollout") )
|
||||
public class Rollout extends NamedEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@OneToMany(targetEntity = RolloutGroup.class)
|
||||
@JoinColumn(name = "rollout", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollout_rolloutgroup") )
|
||||
private List<RolloutGroup> rolloutGroups;
|
||||
|
||||
@Column(name = "target_filter", length = 1024, nullable = false)
|
||||
private String targetFilterQuery;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "distribution_set", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolltout_ds") )
|
||||
private DistributionSet distributionSet;
|
||||
|
||||
@Column(name = "status")
|
||||
private RolloutStatus status = RolloutStatus.CREATING;
|
||||
|
||||
@Column(name = "last_check")
|
||||
private long lastCheck = 0L;
|
||||
|
||||
@Column(name = "action_type", nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ActionType actionType = ActionType.FORCED;
|
||||
|
||||
@Column(name = "forced_time")
|
||||
private long forcedTime;
|
||||
|
||||
@Column(name = "total_targets")
|
||||
private long totalTargets;
|
||||
|
||||
@Transient
|
||||
@CacheField(key = CacheKeys.ROLLOUT_GROUP_TOTAL)
|
||||
private int rolloutGroupsTotal = 0;
|
||||
|
||||
@Transient
|
||||
@CacheField(key = CacheKeys.ROLLOUT_GROUP_CREATED)
|
||||
private int rolloutGroupsCreated = 0;
|
||||
|
||||
@Transient
|
||||
private TotalTargetCountStatus totalTargetCountStatus;
|
||||
|
||||
/**
|
||||
* @return the distributionSet
|
||||
*/
|
||||
public DistributionSet getDistributionSet() {
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param distributionSet
|
||||
* the distributionSet to set
|
||||
*/
|
||||
public void setDistributionSet(final DistributionSet distributionSet) {
|
||||
this.distributionSet = distributionSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rolloutGroups
|
||||
*/
|
||||
public List<RolloutGroup> getRolloutGroups() {
|
||||
return rolloutGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rolloutGroups
|
||||
* the rolloutGroups to set
|
||||
*/
|
||||
public void setRolloutGroups(final List<RolloutGroup> rolloutGroups) {
|
||||
this.rolloutGroups = rolloutGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the targetFilterQuery
|
||||
*/
|
||||
public String getTargetFilterQuery() {
|
||||
return targetFilterQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param targetFilterQuery
|
||||
* the targetFilterQuery to set
|
||||
*/
|
||||
public void setTargetFilterQuery(final String targetFilterQuery) {
|
||||
this.targetFilterQuery = targetFilterQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the status
|
||||
*/
|
||||
public RolloutStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param status
|
||||
* the status to set
|
||||
*/
|
||||
public void setStatus(final RolloutStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the lastCheck
|
||||
*/
|
||||
public long getLastCheck() {
|
||||
return lastCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lastCheck
|
||||
* the lastCheck to set
|
||||
*/
|
||||
public void setLastCheck(final long lastCheck) {
|
||||
this.lastCheck = lastCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the actionType
|
||||
*/
|
||||
public ActionType getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param actionType
|
||||
* the actionType to set
|
||||
*/
|
||||
public void setActionType(final ActionType actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the forcedTime
|
||||
*/
|
||||
public long getForcedTime() {
|
||||
return forcedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param forcedTime
|
||||
* the forcedTime to set
|
||||
*/
|
||||
public void setForcedTime(final long forcedTime) {
|
||||
this.forcedTime = forcedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the totalTargets
|
||||
*/
|
||||
public long getTotalTargets() {
|
||||
return totalTargets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param totalTargets
|
||||
* the totalTargets to set
|
||||
*/
|
||||
public void setTotalTargets(final long totalTargets) {
|
||||
this.totalTargets = totalTargets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rolloutGroupsTotal
|
||||
*/
|
||||
public int getRolloutGroupsTotal() {
|
||||
return rolloutGroupsTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rolloutGroupsTotal
|
||||
* the rolloutGroupsTotal to set
|
||||
*/
|
||||
public void setRolloutGroupsTotal(final int rolloutGroupsTotal) {
|
||||
this.rolloutGroupsTotal = rolloutGroupsTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rolloutGroupsCreated
|
||||
*/
|
||||
public int getRolloutGroupsCreated() {
|
||||
return rolloutGroupsCreated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rolloutGroupsCreated
|
||||
* the rolloutGroupsCreated to set
|
||||
*/
|
||||
public void setRolloutGroupsCreated(final int rolloutGroupsCreated) {
|
||||
this.rolloutGroupsCreated = rolloutGroupsCreated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the totalTargetCountStatus
|
||||
*/
|
||||
public TotalTargetCountStatus getTotalTargetCountStatus() {
|
||||
if (totalTargetCountStatus == null) {
|
||||
this.totalTargetCountStatus = new TotalTargetCountStatus(totalTargets);
|
||||
}
|
||||
return totalTargetCountStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param totalTargetCountStatus
|
||||
* the totalTargetCountStatus to set
|
||||
*/
|
||||
public void setTotalTargetCountStatus(final TotalTargetCountStatus totalTargetCountStatus) {
|
||||
this.totalTargetCountStatus = totalTargetCountStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Rollout [rolloutGroups=" + rolloutGroups + ", targetFilterQuery=" + targetFilterQuery
|
||||
+ ", distributionSet=" + distributionSet + ", status=" + status + ", lastCheck=" + lastCheck
|
||||
+ ", getName()=" + getName() + ", getId()=" + getId() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
public static enum RolloutStatus {
|
||||
|
||||
/**
|
||||
* Rollouts is beeing created.
|
||||
*/
|
||||
CREATING,
|
||||
|
||||
/**
|
||||
* Rollout is ready to start.
|
||||
*/
|
||||
READY,
|
||||
|
||||
/**
|
||||
* Rollout is paused.
|
||||
*/
|
||||
PAUSED,
|
||||
|
||||
/**
|
||||
* Rollout is starting.
|
||||
*/
|
||||
STARTING,
|
||||
|
||||
/**
|
||||
* Rollout is stopped.
|
||||
*/
|
||||
STOPPED,
|
||||
|
||||
/**
|
||||
* Rollout is running.
|
||||
*/
|
||||
RUNNING,
|
||||
|
||||
/**
|
||||
* Rollout is finished.
|
||||
*/
|
||||
FINISHED,
|
||||
|
||||
/**
|
||||
* Rollout could not created due errors, might be database problem due
|
||||
* asynchronous creating.
|
||||
*/
|
||||
ERROR_CREATING,
|
||||
|
||||
/**
|
||||
* Rollout could not started due errors, might be database problem due
|
||||
* asynchronous starting.
|
||||
*/
|
||||
ERROR_STARTING;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
/**
|
||||
* 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.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
/**
|
||||
* JPA entity definition of persisting a group of an rollout.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_rolloutgroup", indexes = {
|
||||
@Index(name = "sp_idx_rolloutgroup_01", columnList = "tenant,name") }, uniqueConstraints = @UniqueConstraint(columnNames = {
|
||||
"name", "rollout", "tenant" }, name = "uk_rolloutgroup") )
|
||||
public class RolloutGroup extends NamedEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "rollout", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolloutgroup_rollout") )
|
||||
private Rollout rollout;
|
||||
|
||||
@Column(name = "status")
|
||||
private RolloutGroupStatus status = RolloutGroupStatus.READY;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
|
||||
@JoinColumn(name = "rolloutGroup_Id", insertable = false, updatable = false)
|
||||
private final List<RolloutTargetGroup> rolloutTargetGroup = new ArrayList<>();
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
private RolloutGroup parent;
|
||||
|
||||
@Column(name = "success_condition", nullable = false)
|
||||
private RolloutGroupSuccessCondition successCondition = RolloutGroupSuccessCondition.THRESHOLD;
|
||||
|
||||
@Column(name = "success_condition_exp", length = 512, nullable = false)
|
||||
private String successConditionExp = null;
|
||||
|
||||
@Column(name = "success_action", nullable = false)
|
||||
private RolloutGroupSuccessAction successAction = RolloutGroupSuccessAction.NEXTGROUP;
|
||||
|
||||
@Column(name = "success_action_exp", length = 512, nullable = false)
|
||||
private String successActionExp = null;
|
||||
|
||||
@Column(name = "error_condition")
|
||||
private RolloutGroupErrorCondition errorCondition = null;
|
||||
|
||||
@Column(name = "error_condition_exp", length = 512)
|
||||
private String errorConditionExp = null;
|
||||
|
||||
@Column(name = "error_action")
|
||||
private RolloutGroupErrorAction errorAction = null;
|
||||
|
||||
@Column(name = "error_action_exp", length = 512)
|
||||
private String errorActionExp = null;
|
||||
|
||||
@Column(name = "total_targets")
|
||||
private long totalTargets;
|
||||
|
||||
@Transient
|
||||
private transient TotalTargetCountStatus totalTargetCountStatus;
|
||||
|
||||
public Rollout getRollout() {
|
||||
return rollout;
|
||||
}
|
||||
|
||||
public void setRollout(final Rollout rollout) {
|
||||
this.rollout = rollout;
|
||||
}
|
||||
|
||||
public RolloutGroupStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(final RolloutGroupStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public List<RolloutTargetGroup> getRolloutTargetGroup() {
|
||||
return rolloutTargetGroup;
|
||||
}
|
||||
|
||||
public RolloutGroup getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(final RolloutGroup parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public RolloutGroupSuccessCondition getSuccessCondition() {
|
||||
return successCondition;
|
||||
}
|
||||
|
||||
public void setSuccessCondition(final RolloutGroupSuccessCondition finishCondition) {
|
||||
this.successCondition = finishCondition;
|
||||
}
|
||||
|
||||
public String getSuccessConditionExp() {
|
||||
return successConditionExp;
|
||||
}
|
||||
|
||||
public void setSuccessConditionExp(final String finishExp) {
|
||||
this.successConditionExp = finishExp;
|
||||
}
|
||||
|
||||
public RolloutGroupErrorCondition getErrorCondition() {
|
||||
return errorCondition;
|
||||
}
|
||||
|
||||
public void setErrorCondition(final RolloutGroupErrorCondition errorCondition) {
|
||||
this.errorCondition = errorCondition;
|
||||
}
|
||||
|
||||
public String getErrorConditionExp() {
|
||||
return errorConditionExp;
|
||||
}
|
||||
|
||||
public void setErrorConditionExp(final String errorExp) {
|
||||
this.errorConditionExp = errorExp;
|
||||
}
|
||||
|
||||
public RolloutGroupErrorAction getErrorAction() {
|
||||
return errorAction;
|
||||
}
|
||||
|
||||
public void setErrorAction(final RolloutGroupErrorAction errorAction) {
|
||||
this.errorAction = errorAction;
|
||||
}
|
||||
|
||||
public String getErrorActionExp() {
|
||||
return errorActionExp;
|
||||
}
|
||||
|
||||
public void setErrorActionExp(final String errorActionExp) {
|
||||
this.errorActionExp = errorActionExp;
|
||||
}
|
||||
|
||||
public RolloutGroupSuccessAction getSuccessAction() {
|
||||
return successAction;
|
||||
}
|
||||
|
||||
public String getSuccessActionExp() {
|
||||
return successActionExp;
|
||||
}
|
||||
|
||||
public long getTotalTargets() {
|
||||
return totalTargets;
|
||||
}
|
||||
|
||||
public void setTotalTargets(final long totalTargets) {
|
||||
this.totalTargets = totalTargets;
|
||||
}
|
||||
|
||||
public void setSuccessAction(final RolloutGroupSuccessAction successAction) {
|
||||
this.successAction = successAction;
|
||||
}
|
||||
|
||||
public void setSuccessActionExp(final String successActionExp) {
|
||||
this.successActionExp = successActionExp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the totalTargetCountStatus
|
||||
*/
|
||||
public TotalTargetCountStatus getTotalTargetCountStatus() {
|
||||
if (totalTargetCountStatus == null) {
|
||||
this.totalTargetCountStatus = new TotalTargetCountStatus(totalTargets);
|
||||
}
|
||||
return totalTargetCountStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param totalTargetCountStatus
|
||||
* the totalTargetCountStatus to set
|
||||
*/
|
||||
public void setTotalTargetCountStatus(final TotalTargetCountStatus totalTargetCountStatus) {
|
||||
this.totalTargetCountStatus = totalTargetCountStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RolloutGroup [rollout=" + rollout + ", status=" + status + ", rolloutTargetGroup=" + rolloutTargetGroup
|
||||
+ ", parent=" + parent + ", finishCondition=" + successCondition + ", finishExp=" + successConditionExp
|
||||
+ ", errorCondition=" + errorCondition + ", errorExp=" + errorConditionExp + ", getName()=" + getName()
|
||||
+ ", getId()=" + getId() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
public enum RolloutGroupStatus {
|
||||
|
||||
/**
|
||||
* Ready to start the group.
|
||||
*/
|
||||
READY,
|
||||
|
||||
/**
|
||||
* Group is scheduled and started sometime, e.g. trigger of group
|
||||
* before.
|
||||
*/
|
||||
SCHEDULED,
|
||||
|
||||
/**
|
||||
* Group is finished.
|
||||
*/
|
||||
FINISHED,
|
||||
|
||||
/**
|
||||
* Group is finished and has errors.
|
||||
*/
|
||||
ERROR,
|
||||
|
||||
/**
|
||||
* Group is running.
|
||||
*/
|
||||
RUNNING;
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to evaluate if an group is success state.
|
||||
*/
|
||||
public enum RolloutGroupSuccessCondition {
|
||||
THRESHOLD("thresholdRolloutGroupSuccessCondition");
|
||||
|
||||
private final String beanName;
|
||||
|
||||
private RolloutGroupSuccessCondition(final String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the beanName
|
||||
*/
|
||||
public String getBeanName() {
|
||||
return beanName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to evaluate if an group is in error state.
|
||||
*/
|
||||
public enum RolloutGroupErrorCondition {
|
||||
THRESHOLD("thresholdRolloutGroupErrorCondition");
|
||||
|
||||
private final String beanName;
|
||||
|
||||
private RolloutGroupErrorCondition(final String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the beanName
|
||||
*/
|
||||
public String getBeanName() {
|
||||
return beanName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The actions executed when the {@link RolloutGroup#errorCondition} is hit.
|
||||
*/
|
||||
public enum RolloutGroupErrorAction {
|
||||
PAUSE("pauseRolloutGroupAction");
|
||||
|
||||
private final String beanName;
|
||||
|
||||
private RolloutGroupErrorAction(final String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the beanName
|
||||
*/
|
||||
public String getBeanName() {
|
||||
return beanName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The actions executed when the {@link RolloutGroup#successCondition} is
|
||||
* hit.
|
||||
*/
|
||||
public enum RolloutGroupSuccessAction {
|
||||
NEXTGROUP("startNextRolloutGroupAction");
|
||||
|
||||
private final String beanName;
|
||||
|
||||
private RolloutGroupSuccessAction(final String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the beanName
|
||||
*/
|
||||
public String getBeanName() {
|
||||
return beanName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Object which holds all {@link RolloutGroup} conditions together which can
|
||||
* easily built.
|
||||
*/
|
||||
public static class RolloutGroupConditions {
|
||||
private RolloutGroupSuccessCondition successCondition = null;
|
||||
private String successConditionExp = null;
|
||||
private RolloutGroupSuccessAction successAction = null;
|
||||
private String successActionExp = null;
|
||||
private RolloutGroupErrorCondition errorCondition = null;
|
||||
private String errorConditionExp = null;
|
||||
private RolloutGroupErrorAction errorAction = null;
|
||||
private String errorActionExp = null;
|
||||
|
||||
public RolloutGroupSuccessCondition getSuccessCondition() {
|
||||
return successCondition;
|
||||
}
|
||||
|
||||
public void setSuccessCondition(final RolloutGroupSuccessCondition finishCondition) {
|
||||
this.successCondition = finishCondition;
|
||||
}
|
||||
|
||||
public String getSuccessConditionExp() {
|
||||
return successConditionExp;
|
||||
}
|
||||
|
||||
public void setSuccessConditionExp(final String finishConditionExp) {
|
||||
this.successConditionExp = finishConditionExp;
|
||||
}
|
||||
|
||||
public RolloutGroupSuccessAction getSuccessAction() {
|
||||
return successAction;
|
||||
}
|
||||
|
||||
public void setSuccessAction(final RolloutGroupSuccessAction successAction) {
|
||||
this.successAction = successAction;
|
||||
}
|
||||
|
||||
public String getSuccessActionExp() {
|
||||
return successActionExp;
|
||||
}
|
||||
|
||||
public void setSuccessActionExp(final String successActionExp) {
|
||||
this.successActionExp = successActionExp;
|
||||
}
|
||||
|
||||
public RolloutGroupErrorCondition getErrorCondition() {
|
||||
return errorCondition;
|
||||
}
|
||||
|
||||
public void setErrorCondition(final RolloutGroupErrorCondition errorCondition) {
|
||||
this.errorCondition = errorCondition;
|
||||
}
|
||||
|
||||
public String getErrorConditionExp() {
|
||||
return errorConditionExp;
|
||||
}
|
||||
|
||||
public void setErrorConditionExp(final String errorConditionExp) {
|
||||
this.errorConditionExp = errorConditionExp;
|
||||
}
|
||||
|
||||
public RolloutGroupErrorAction getErrorAction() {
|
||||
return errorAction;
|
||||
}
|
||||
|
||||
public void setErrorAction(final RolloutGroupErrorAction errorAction) {
|
||||
this.errorAction = errorAction;
|
||||
}
|
||||
|
||||
public String getErrorActionExp() {
|
||||
return errorActionExp;
|
||||
}
|
||||
|
||||
public void setErrorActionExp(final String errorActionExp) {
|
||||
this.errorActionExp = errorActionExp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder to build easily the {@link RolloutGroupConditions}.
|
||||
*
|
||||
*/
|
||||
public static class RolloutGroupConditionBuilder {
|
||||
private final RolloutGroupConditions conditions = new RolloutGroupConditions();
|
||||
|
||||
public RolloutGroupConditions build() {
|
||||
return conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the finish condition and expression on the builder.
|
||||
*
|
||||
* @param condition
|
||||
* the finish condition
|
||||
* @param expression
|
||||
* the finish expression
|
||||
* @return the builder itself
|
||||
*/
|
||||
public RolloutGroupConditionBuilder successCondition(final RolloutGroupSuccessCondition condition,
|
||||
final String expression) {
|
||||
conditions.setSuccessCondition(condition);
|
||||
conditions.setSuccessConditionExp(expression);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the success action and expression on the builder.
|
||||
*
|
||||
* @param action
|
||||
* the success action
|
||||
* @param expression
|
||||
* the error expression
|
||||
* @return the builder itself
|
||||
*/
|
||||
public RolloutGroupConditionBuilder successAction(final RolloutGroupSuccessAction action,
|
||||
final String expression) {
|
||||
conditions.setSuccessAction(action);
|
||||
conditions.setSuccessActionExp(expression);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error condition and expression on the builder.
|
||||
*
|
||||
* @param condition
|
||||
* the error condition
|
||||
* @param expression
|
||||
* the error expression
|
||||
* @return the builder itself
|
||||
*/
|
||||
public RolloutGroupConditionBuilder errorCondition(final RolloutGroupErrorCondition condition,
|
||||
final String expression) {
|
||||
conditions.setErrorCondition(condition);
|
||||
conditions.setErrorConditionExp(expression);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error action and expression on the builder.
|
||||
*
|
||||
* @param action
|
||||
* the error action
|
||||
* @param expression
|
||||
* the error expression
|
||||
* @return the builder itself
|
||||
*/
|
||||
public RolloutGroupConditionBuilder errorAction(final RolloutGroupErrorAction action, final String expression) {
|
||||
conditions.setErrorAction(action);
|
||||
conditions.setErrorActionExp(expression);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinColumns;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
@IdClass(RolloutTargetGroupId.class)
|
||||
@Entity
|
||||
@Table(name = "sp_rollouttargetgroup")
|
||||
public class RolloutTargetGroup {
|
||||
|
||||
@Id
|
||||
@ManyToOne(targetEntity = RolloutGroup.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
|
||||
@JoinColumn(name = "rolloutGroup_Id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_group"))
|
||||
private RolloutGroup rolloutGroup;
|
||||
|
||||
@Id
|
||||
@ManyToOne(targetEntity = Target.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
|
||||
@JoinColumn(name = "target_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_target"))
|
||||
private Target target;
|
||||
|
||||
@OneToMany(targetEntity = Action.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;
|
||||
|
||||
/**
|
||||
* default constructor for JPA.
|
||||
*/
|
||||
public RolloutTargetGroup() {
|
||||
// JPA constructor
|
||||
}
|
||||
|
||||
public RolloutTargetGroup(final RolloutGroup rolloutGroup, final Target target) {
|
||||
this.rolloutGroup = rolloutGroup;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public RolloutTargetGroupId getId() {
|
||||
return new RolloutTargetGroupId(rolloutGroup, target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Combined unique key of the table {@link RolloutTargetGroup}.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
public class RolloutTargetGroupId implements Serializable {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long rolloutGroup;
|
||||
private Long target;
|
||||
|
||||
/**
|
||||
* default constructor necessary for JPA.
|
||||
*/
|
||||
public RolloutTargetGroupId() {
|
||||
// default constructor necessary for JPA, empty.
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param rolloutGroup
|
||||
* the rollout group for this key
|
||||
* @param target
|
||||
* the target for this key
|
||||
*/
|
||||
public RolloutTargetGroupId(final RolloutGroup rolloutGroup, final Target target) {
|
||||
this.rolloutGroup = rolloutGroup.getId();
|
||||
this.target = target.getId();
|
||||
}
|
||||
|
||||
public Long getRolloutGroup() {
|
||||
return rolloutGroup;
|
||||
}
|
||||
|
||||
public Long getTarget() {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public class SoftwareModuleMetadata implements Serializable {
|
||||
|
||||
@Id
|
||||
@ManyToOne(targetEntity = SoftwareModule.class, fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "sw_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_sw") )
|
||||
@JoinColumn(name = "sw_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_sw"))
|
||||
private SoftwareModule softwareModule;
|
||||
|
||||
public SoftwareModuleMetadata() {
|
||||
|
||||
@@ -106,6 +106,11 @@ public class Target extends NamedEntity implements Persistable<Long> {
|
||||
@Column(name = "sec_token", insertable = true, updatable = true, nullable = false, length = 128)
|
||||
private String securityToken = null;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE, CascadeType.PERSIST })
|
||||
@JoinColumn(name = "target_Id", insertable = false, updatable = false)
|
||||
private final List<RolloutTargetGroup> rolloutTargetGroup = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.model;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
|
||||
/**
|
||||
*
|
||||
* Rollout - Target with action status.
|
||||
*
|
||||
*/
|
||||
public class TargetWithActionStatus {
|
||||
|
||||
private Target target;
|
||||
|
||||
private Status status = null;;
|
||||
|
||||
public TargetWithActionStatus(final Target target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public TargetWithActionStatus(final Target target, final Status status) {
|
||||
this.status = status;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the target
|
||||
*/
|
||||
public Target getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the status
|
||||
*/
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param target
|
||||
* the target to set
|
||||
*/
|
||||
public void setTarget(final Target target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param status
|
||||
* the status to set
|
||||
*/
|
||||
public void setStatus(final Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.model;
|
||||
|
||||
/**
|
||||
* Represents rollout or rollout group statuses and count of targets in each
|
||||
* status.
|
||||
*
|
||||
*/
|
||||
public class TotalTargetCountActionStatus {
|
||||
|
||||
private final Action.Status status;
|
||||
private final Long count;
|
||||
private Long id;
|
||||
|
||||
public TotalTargetCountActionStatus(final Long id, final Action.Status status, final Long count) {
|
||||
this.status = status;
|
||||
this.count = count;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public TotalTargetCountActionStatus(final Action.Status status, final Long count) {
|
||||
this.status = status;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the id
|
||||
*/
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the status
|
||||
*/
|
||||
public Action.Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the count
|
||||
*/
|
||||
public Long getCount() {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* 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.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* Store all states with the target count of a rollout or rolloutgroup.
|
||||
*
|
||||
*/
|
||||
public class TotalTargetCountStatus {
|
||||
|
||||
/**
|
||||
* Status of the total target counts.
|
||||
*/
|
||||
public enum Status {
|
||||
SCHEDULED, RUNNING, ERROR, FINISHED, CANCELLED, NOTSTARTED
|
||||
}
|
||||
|
||||
private final Map<Status, Long> statusTotalCountMap = new HashMap<>();
|
||||
private final Long totalTargetCount;
|
||||
|
||||
/**
|
||||
* Create a new states map with the target count for each state.
|
||||
*
|
||||
* @param targetCountActionStatus
|
||||
* the action state map
|
||||
* @param totalTargets
|
||||
* the total target count
|
||||
*/
|
||||
public TotalTargetCountStatus(final List<TotalTargetCountActionStatus> targetCountActionStatus,
|
||||
final Long totalTargetCount) {
|
||||
this.totalTargetCount = totalTargetCount;
|
||||
mapActionStatusToTotalTargetCountStatus(targetCountActionStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new states map with the target count for each state.
|
||||
*
|
||||
* @param totalTargetCount
|
||||
* the total target count
|
||||
*/
|
||||
public TotalTargetCountStatus(final Long totalTargetCount) {
|
||||
this(Collections.emptyList(), totalTargetCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* The current state mape which the total target count
|
||||
*
|
||||
* @return the statusTotalCountMap the state map
|
||||
*/
|
||||
public Map<Status, Long> getStatusTotalCountMap() {
|
||||
return statusTotalCountMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total target count from a state.
|
||||
*
|
||||
* @param status
|
||||
* the state key
|
||||
* @return the current target count cannot be <null>
|
||||
*/
|
||||
public Long getTotalTargetCountByStatus(final Status status) {
|
||||
final Long count = statusTotalCountMap.get(status);
|
||||
return count == null ? 0L : count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate all target status to a the given map
|
||||
*
|
||||
* @param statusTotalCountMap
|
||||
* the map
|
||||
* @param rolloutStatusCountItems
|
||||
* all target statut with total count
|
||||
* @return <true> some state is populated <false> nothing is happend
|
||||
*/
|
||||
private final void mapActionStatusToTotalTargetCountStatus(
|
||||
final List<TotalTargetCountActionStatus> targetCountActionStatus) {
|
||||
if (targetCountActionStatus == null) {
|
||||
statusTotalCountMap.put(TotalTargetCountStatus.Status.NOTSTARTED, totalTargetCount);
|
||||
return;
|
||||
}
|
||||
statusTotalCountMap.put(Status.RUNNING, 0L);
|
||||
Long notStartedTargetCount = totalTargetCount;
|
||||
for (final TotalTargetCountActionStatus item : targetCountActionStatus) {
|
||||
switch (item.getStatus()) {
|
||||
case SCHEDULED:
|
||||
statusTotalCountMap.put(Status.SCHEDULED, item.getCount());
|
||||
break;
|
||||
case ERROR:
|
||||
statusTotalCountMap.put(Status.ERROR, item.getCount());
|
||||
break;
|
||||
case FINISHED:
|
||||
statusTotalCountMap.put(Status.FINISHED, item.getCount());
|
||||
break;
|
||||
case RETRIEVED:
|
||||
case RUNNING:
|
||||
case WARNING:
|
||||
case DOWNLOAD:
|
||||
case CANCELING:
|
||||
final Long runningItemsCount = statusTotalCountMap.get(Status.RUNNING) + item.getCount();
|
||||
statusTotalCountMap.put(Status.RUNNING, runningItemsCount);
|
||||
break;
|
||||
case CANCELED:
|
||||
statusTotalCountMap.put(Status.CANCELLED, item.getCount());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("State " + item.getStatus() + "is not valid");
|
||||
}
|
||||
notStartedTargetCount -= item.getCount();
|
||||
}
|
||||
statusTotalCountMap.put(TotalTargetCountStatus.Status.NOTSTARTED, notStartedTargetCount);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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.model.helper;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.EntityPropertyChangeListener;
|
||||
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds the {@link AfterTransactionCommitExecutor} to
|
||||
* have to the cache manager in beans not instantiated by spring e.g. JPA
|
||||
* entities or {@link EntityPropertyChangeListener} which cannot be autowired.
|
||||
*
|
||||
*/
|
||||
public final class AfterTransactionCommitExecutorHolder {
|
||||
|
||||
private static final AfterTransactionCommitExecutorHolder SINGLETON = new AfterTransactionCommitExecutorHolder();
|
||||
|
||||
@Autowired
|
||||
private AfterTransactionCommitExecutor afterCommit;
|
||||
|
||||
private AfterTransactionCommitExecutorHolder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cache manager holder singleton instance
|
||||
*/
|
||||
public static AfterTransactionCommitExecutorHolder getInstance() {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the afterCommit
|
||||
*/
|
||||
public AfterTransactionCommitExecutor getAfterCommit() {
|
||||
return afterCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param afterCommit
|
||||
* the afterCommit to set
|
||||
*/
|
||||
public void setAfterCommit(final AfterTransactionCommitExecutor afterCommit) {
|
||||
this.afterCommit = afterCommit;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.model.helper;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.CacheFieldEntityListener;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds the {@link EventBus} to have to the cache
|
||||
* manager in beans not instantiated by spring e.g. JPA entities or
|
||||
* {@link CacheFieldEntityListener} which cannot be autowired.
|
||||
*
|
||||
*/
|
||||
public final class EventBusHolder {
|
||||
|
||||
private static final EventBusHolder SINGLETON = new EventBusHolder();
|
||||
|
||||
@Autowired
|
||||
private EventBus eventBus;
|
||||
|
||||
private EventBusHolder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cache manager holder singleton instance
|
||||
*/
|
||||
public static EventBusHolder getInstance() {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the eventBus
|
||||
*/
|
||||
public EventBus getEventBus() {
|
||||
return eventBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param eventBus
|
||||
* the eventBus to set
|
||||
*/
|
||||
public void setEventBus(final EventBus eventBus) {
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -104,6 +104,30 @@ public final class RSQLUtility {
|
||||
return new RSQLSpecification<>(rsql, fieldNameProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given rsql string regarding existence and correct syntax.
|
||||
*
|
||||
* @param rsql
|
||||
* the rsql string to get validated
|
||||
*
|
||||
*/
|
||||
public static void isValid(final String rsql) {
|
||||
parseRsql(rsql);
|
||||
}
|
||||
|
||||
private static Node parseRsql(final String rsql) {
|
||||
try {
|
||||
LOGGER.debug("parsing rsql string {}", rsql);
|
||||
final Set<ComparisonOperator> operators = RSQLOperators.defaultOperators();
|
||||
operators.add(new ComparisonOperator("=li=", false));
|
||||
return new RSQLParser(operators).parse(rsql);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
throw new RSQLParameterSyntaxException("rsql filter must not be null", e);
|
||||
} catch (final RSQLParserException e) {
|
||||
throw new RSQLParameterSyntaxException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RSQLSpecification<A extends Enum<A> & FieldNameProvider, T> implements Specification<T> {
|
||||
|
||||
private final String rsql;
|
||||
@@ -125,15 +149,7 @@ public final class RSQLUtility {
|
||||
@Override
|
||||
public Predicate toPredicate(final Root<T> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
|
||||
|
||||
final Node rootNode;
|
||||
try {
|
||||
LOGGER.debug("parsing rsql string {}", rsql);
|
||||
final Set<ComparisonOperator> operators = RSQLOperators.defaultOperators();
|
||||
operators.add(new ComparisonOperator("=li=", false));
|
||||
rootNode = new RSQLParser(operators).parse(rsql);
|
||||
} catch (final RSQLParserException e) {
|
||||
throw new RSQLParameterSyntaxException(e);
|
||||
}
|
||||
final Node rootNode = parseRsql(rsql);
|
||||
|
||||
final JpqQueryRSQLVisitor<A, T> jpqQueryRSQLVisitor = new JpqQueryRSQLVisitor<>(root, cb, enumType);
|
||||
final List<Predicate> accept = rootNode.<List<Predicate>, String> accept(jpqQueryRSQLVisitor);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.rollout.condition;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.eclipse.hawkbit.repository.RolloutGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Error action evaluator which pauses the whole {@link Rollout} and sets the
|
||||
* current {@link RolloutGroup} to error.
|
||||
*/
|
||||
@Component("pauseRolloutGroupAction")
|
||||
public class PauseRolloutGroupAction implements RolloutGroupActionEvaluator {
|
||||
|
||||
@Autowired
|
||||
private RolloutManagement rolloutManagement;
|
||||
|
||||
@Autowired
|
||||
private RolloutGroupRepository rolloutGroupRepository;
|
||||
|
||||
@Autowired
|
||||
private SystemSecurityContext systemSecurityContext;
|
||||
|
||||
@Override
|
||||
public boolean verifyExpression(final String expression) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eval(final Rollout rollout, final RolloutGroup rolloutGroup, final String expression) {
|
||||
systemSecurityContext.runAsSystem(new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
rolloutGroup.setStatus(RolloutGroupStatus.ERROR);
|
||||
rolloutGroupRepository.save(rolloutGroup);
|
||||
rolloutManagement.pauseRollout(rollout);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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.rollout.condition;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface RolloutGroupActionEvaluator {
|
||||
|
||||
boolean verifyExpression(final String expression);
|
||||
|
||||
void eval(Rollout rollout, RolloutGroup rolloutGroup, final String expression);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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.rollout.condition;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface RolloutGroupConditionEvaluator {
|
||||
|
||||
boolean verifyExpression(final String expression);
|
||||
|
||||
boolean eval(Rollout rollout, RolloutGroup rolloutGroup, final String expression);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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.rollout.condition;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Success action which starts the next following {@link RolloutGroup}.
|
||||
*/
|
||||
@Component("startNextRolloutGroupAction")
|
||||
public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActionEvaluator {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(StartNextGroupRolloutGroupSuccessAction.class);
|
||||
|
||||
@Autowired
|
||||
private RolloutGroupRepository rolloutGroupRepository;
|
||||
|
||||
@Autowired
|
||||
private DeploymentManagement deploymentManagement;
|
||||
|
||||
@Autowired
|
||||
private SystemSecurityContext systemSecurityContext;
|
||||
|
||||
@Override
|
||||
public boolean verifyExpression(final String expression) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eval(final Rollout rollout, final RolloutGroup rolloutGroup, final String expression) {
|
||||
systemSecurityContext.runAsSystem(() -> {
|
||||
startNextGroup(rollout, rolloutGroup);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void startNextGroup(final Rollout rollout, final RolloutGroup rolloutGroup) {
|
||||
// retrieve all actions accroding to the parent group of the finished
|
||||
// rolloutGroup, so retrieve all child-group actions which need to be
|
||||
// started.
|
||||
final List<Action> rolloutGroupActions = deploymentManagement.findActionsByRolloutGroupParentAndStatus(rollout,
|
||||
rolloutGroup, Action.Status.SCHEDULED);
|
||||
logger.debug("{} Next actions to start for rollout {} and parent group {}", rolloutGroupActions.size(), rollout,
|
||||
rolloutGroup);
|
||||
rolloutGroupActions.forEach(action -> deploymentManagement.startScheduledAction(action));
|
||||
if (!rolloutGroupActions.isEmpty()) {
|
||||
// get all next scheduled groups based on the found actions and set
|
||||
// them in state running
|
||||
rolloutGroupActions.forEach(action -> {
|
||||
final RolloutGroup nextGroup = action.getRolloutGroup();
|
||||
logger.debug("Rolloutgroup {} is now running", nextGroup);
|
||||
nextGroup.setStatus(RolloutGroupStatus.RUNNING);
|
||||
rolloutGroupRepository.save(nextGroup);
|
||||
});
|
||||
} else {
|
||||
logger.info("No actions to start for next rolloutgroup of parent {}", rolloutGroup);
|
||||
// nothing for next group, just finish the group, this can happen
|
||||
// e.g. if targets has been deleted after the group has been
|
||||
// scheduled. If the group is empty now, we just finish the group if
|
||||
// there are not actions available for this group.
|
||||
final List<RolloutGroup> findByRolloutGroupParent = rolloutGroupRepository
|
||||
.findByParentAndStatus(rolloutGroup, RolloutGroupStatus.SCHEDULED);
|
||||
findByRolloutGroupParent.forEach(nextGroup -> {
|
||||
logger.debug("Rolloutgroup {} is finished, starting next group", nextGroup);
|
||||
nextGroup.setStatus(RolloutGroupStatus.FINISHED);
|
||||
rolloutGroupRepository.save(nextGroup);
|
||||
// find the next group to set in running state
|
||||
startNextGroup(rollout, nextGroup);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.rollout.condition;
|
||||
|
||||
import org.eclipse.hawkbit.repository.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Component("thresholdRolloutGroupErrorCondition")
|
||||
public class ThresholdRolloutGroupErrorCondition implements RolloutGroupConditionEvaluator {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ThresholdRolloutGroupErrorCondition.class);
|
||||
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
|
||||
@Override
|
||||
public boolean verifyExpression(final String expression) {
|
||||
// percentage value between 0 and 100
|
||||
try {
|
||||
final Integer value = Integer.valueOf(expression);
|
||||
if (value >= 0 || value <= 100) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
} catch (final RuntimeException e) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean eval(final Rollout rollout, final RolloutGroup rolloutGroup, final String expression) {
|
||||
final Long totalGroup = actionRepository.countByRolloutAndRolloutGroup(rollout, rolloutGroup);
|
||||
final Long error = actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rollout.getId(),
|
||||
rolloutGroup.getId(), Action.Status.ERROR);
|
||||
try {
|
||||
final Integer threshold = Integer.valueOf(expression);
|
||||
|
||||
if (totalGroup == 0) {
|
||||
// in case e.g. targets has been deleted we don't have any
|
||||
// actions left for this group, so the group is finished
|
||||
return false;
|
||||
}
|
||||
|
||||
// calculate threshold
|
||||
return ((float) error / (float) totalGroup) > ((float) threshold / 100F);
|
||||
} catch (final NumberFormatException e) {
|
||||
logger.error("Cannot evaluate condition expression " + expression, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.rollout.condition;
|
||||
|
||||
import org.eclipse.hawkbit.repository.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Component("thresholdRolloutGroupSuccessCondition")
|
||||
public class ThresholdRolloutGroupSuccessCondition implements RolloutGroupConditionEvaluator {
|
||||
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
|
||||
@Override
|
||||
public boolean verifyExpression(final String expression) {
|
||||
// percentage value between 0 and 100
|
||||
try {
|
||||
final Integer value = Integer.valueOf(expression);
|
||||
if (value >= 0 || value <= 100) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
} catch (final RuntimeException e) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean eval(final Rollout rollout, final RolloutGroup rolloutGroup, final String expression) {
|
||||
final Long totalGroup = rolloutGroup.getTotalTargets();
|
||||
final Long finished = actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rollout.getId(),
|
||||
rolloutGroup.getId(), Action.Status.FINISHED);
|
||||
final Integer threshold = Integer.valueOf(expression);
|
||||
|
||||
if (totalGroup == 0) {
|
||||
// in case e.g. targets has been deleted we don't have any actions
|
||||
// left for this group, so the group is finished
|
||||
return true;
|
||||
}
|
||||
// calculate threshold
|
||||
return ((float) finished / (float) totalGroup) >= ((float) threshold / 100F);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
create table sp_rolloutgroup (
|
||||
id bigint generated by default as identity,
|
||||
created_at bigint,
|
||||
created_by varchar(40),
|
||||
last_modified_at bigint,
|
||||
last_modified_by varchar(40),
|
||||
optlock_revision bigint,
|
||||
tenant varchar(40) not null,
|
||||
description varchar(512),
|
||||
name varchar(64) not null,
|
||||
error_condition integer,
|
||||
error_condition_exp varchar(512),
|
||||
error_action integer,
|
||||
error_action_exp varchar(512),
|
||||
success_condition integer not null,
|
||||
success_condition_exp varchar(512) not null,
|
||||
success_action integer not null,
|
||||
success_action_exp varchar(512),
|
||||
status integer,
|
||||
parent_id bigint,
|
||||
rollout bigint,
|
||||
total_targets bigint,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table sp_rollout (
|
||||
id bigint generated by default as identity,
|
||||
created_at bigint,
|
||||
created_by varchar(40),
|
||||
last_modified_at bigint,
|
||||
last_modified_by varchar(40),
|
||||
optlock_revision bigint,
|
||||
tenant varchar(40) not null,
|
||||
description varchar(512),
|
||||
name varchar(64) not null,
|
||||
last_check bigint,
|
||||
group_theshold float,
|
||||
status integer,
|
||||
distribution_set bigint,
|
||||
target_filter varchar(1024),
|
||||
action_type varchar(255) not null,
|
||||
forced_time bigint,
|
||||
total_targets bigint,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table sp_rollouttargetgroup (
|
||||
target_Id bigint not null,
|
||||
rolloutGroup_Id bigint not null,
|
||||
primary key (rolloutGroup_Id, target_Id)
|
||||
);
|
||||
|
||||
create index sp_idx_rollout_01 on sp_rollout (tenant, name);
|
||||
|
||||
create index sp_idx_rolloutgroup_01 on sp_rolloutgroup (tenant, name);
|
||||
|
||||
ALTER TABLE sp_action ADD COLUMN rollout bigint;
|
||||
ALTER TABLE sp_action ADD COLUMN rolloutgroup bigint;
|
||||
|
||||
alter table sp_rollout
|
||||
add constraint uk_rollout unique (name, tenant);
|
||||
|
||||
alter table sp_rolloutgroup
|
||||
add constraint uk_rolloutgroup unique (name, rollout, tenant);
|
||||
|
||||
alter table sp_action
|
||||
add constraint fk_action_rollout
|
||||
foreign key (rollout)
|
||||
references sp_rollout;
|
||||
|
||||
alter table sp_action
|
||||
add constraint fk_action_rolloutgroup
|
||||
foreign key (rolloutgroup)
|
||||
references sp_rolloutgroup;
|
||||
|
||||
alter table sp_rollout
|
||||
add constraint fk_rollout_ds
|
||||
foreign key (distribution_set)
|
||||
references sp_distribution_set;
|
||||
|
||||
alter table sp_rolloutgroup
|
||||
add constraint fk_rolloutgroup_rollout
|
||||
foreign key (rollout)
|
||||
references sp_rollout
|
||||
on delete cascade;
|
||||
|
||||
alter table sp_rolloutgroup
|
||||
add constraint fk_rolloutgroup_rolloutgroup
|
||||
foreign key (parent_id)
|
||||
references sp_rolloutgroup
|
||||
on delete cascade;
|
||||
|
||||
alter table sp_rollouttargetgroup
|
||||
add constraint fk_rollouttargetgroup_target
|
||||
foreign key (target_id)
|
||||
references sp_target
|
||||
on delete cascade;
|
||||
|
||||
alter table sp_rollouttargetgroup
|
||||
add constraint fk_rollouttargetgroup_rolloutgroup
|
||||
foreign key (rolloutgroup_id)
|
||||
references sp_rolloutgroup
|
||||
on delete cascade;
|
||||
@@ -0,0 +1,103 @@
|
||||
create table sp_rolloutgroup (
|
||||
id bigint not null auto_increment,
|
||||
created_at bigint,
|
||||
created_by varchar(40),
|
||||
last_modified_at bigint,
|
||||
last_modified_by varchar(40),
|
||||
optlock_revision bigint,
|
||||
tenant varchar(40) not null,
|
||||
description varchar(512),
|
||||
name varchar(64) not null,
|
||||
error_condition integer,
|
||||
error_condition_exp varchar(512),
|
||||
error_action integer,
|
||||
error_action_exp varchar(512),
|
||||
success_condition integer not null,
|
||||
success_condition_exp varchar(512) not null,
|
||||
success_action integer not null,
|
||||
success_action_exp varchar(512),
|
||||
status integer,
|
||||
parent_id bigint,
|
||||
rollout bigint,
|
||||
total_targets bigint,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table sp_rollout (
|
||||
id bigint not null auto_increment,
|
||||
created_at bigint,
|
||||
created_by varchar(40),
|
||||
last_modified_at bigint,
|
||||
last_modified_by varchar(40),
|
||||
optlock_revision bigint,
|
||||
tenant varchar(40) not null,
|
||||
description varchar(512),
|
||||
name varchar(64) not null,
|
||||
last_check bigint,
|
||||
group_theshold float,
|
||||
status integer,
|
||||
distribution_set bigint,
|
||||
target_filter varchar(1024),
|
||||
action_type varchar(255) not null,
|
||||
forced_time bigint,
|
||||
total_targets bigint,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table sp_rollouttargetgroup (
|
||||
target_Id bigint not null,
|
||||
rolloutGroup_Id bigint not null,
|
||||
primary key (rolloutGroup_Id, target_Id)
|
||||
);
|
||||
|
||||
create index sp_idx_rollout_01 on sp_rollout (tenant, name);
|
||||
|
||||
create index sp_idx_rolloutgroup_01 on sp_rolloutgroup (tenant, name);
|
||||
|
||||
ALTER TABLE sp_action ADD COLUMN rollout bigint;
|
||||
ALTER TABLE sp_action ADD COLUMN rolloutgroup bigint;
|
||||
|
||||
alter table sp_rollout
|
||||
add constraint uk_rollout unique (name, tenant);
|
||||
|
||||
alter table sp_rolloutgroup
|
||||
add constraint uk_rolloutgroup unique (name, rollout, tenant);
|
||||
|
||||
alter table sp_action
|
||||
add constraint fk_action_rollout
|
||||
foreign key (rollout)
|
||||
references sp_rollout (id);
|
||||
|
||||
alter table sp_action
|
||||
add constraint fk_action_rolloutgroup
|
||||
foreign key (rolloutgroup)
|
||||
references sp_rolloutgroup (id);
|
||||
|
||||
alter table sp_rollout
|
||||
add constraint fk_rollout_ds
|
||||
foreign key (distribution_set)
|
||||
references sp_distribution_set (id);
|
||||
|
||||
alter table sp_rolloutgroup
|
||||
add constraint fk_rolloutgroup_rollout
|
||||
foreign key (rollout)
|
||||
references sp_rollout (id)
|
||||
on delete cascade;
|
||||
|
||||
alter table sp_rolloutgroup
|
||||
add constraint fk_rolloutgroup_rolloutgroup
|
||||
foreign key (parent_id)
|
||||
references sp_rolloutgroup (id)
|
||||
on delete cascade;
|
||||
|
||||
alter table sp_rollouttargetgroup
|
||||
add constraint fk_rollouttargetgroup_target
|
||||
foreign key (target_id)
|
||||
references sp_target (id)
|
||||
on delete cascade;
|
||||
|
||||
alter table sp_rollouttargetgroup
|
||||
add constraint fk_rollouttargetgroup_rolloutgroup
|
||||
foreign key (rolloutgroup_id)
|
||||
references sp_rolloutgroup (id)
|
||||
on delete cascade;
|
||||
@@ -28,6 +28,9 @@ import org.eclipse.hawkbit.repository.DistributionSetTagRepository;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetTypeRepository;
|
||||
import org.eclipse.hawkbit.repository.ExternalArtifactRepository;
|
||||
import org.eclipse.hawkbit.repository.LocalArtifactRepository;
|
||||
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.SoftwareManagement;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleMetadataRepository;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleRepository;
|
||||
@@ -178,6 +181,15 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware {
|
||||
@Autowired
|
||||
protected TenantAwareCacheManager cacheManager;
|
||||
|
||||
@Autowired
|
||||
protected RolloutManagement rolloutManagement;
|
||||
|
||||
@Autowired
|
||||
protected RolloutGroupManagement rolloutGroupManagement;
|
||||
|
||||
@Autowired
|
||||
protected RolloutGroupRepository rolloutGroupRepository;
|
||||
|
||||
protected MockMvc mvc;
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
package org.eclipse.hawkbit;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.eclipse.hawkbit.cache.CacheConstants;
|
||||
import org.eclipse.hawkbit.cache.TenancyCacheManager;
|
||||
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
|
||||
import org.eclipse.hawkbit.repository.model.helper.EventBusHolder;
|
||||
import org.eclipse.hawkbit.repository.utils.RepositoryDataGenerator;
|
||||
import org.eclipse.hawkbit.repository.utils.RepositoryDataGenerator.DatabaseCleanupUtil;
|
||||
import org.eclipse.hawkbit.security.SecurityContextTenantAware;
|
||||
@@ -30,7 +32,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.security.concurrent.DelegatingSecurityContextExecutor;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
|
||||
import com.google.common.eventbus.AsyncEventBus;
|
||||
@@ -91,9 +93,14 @@ public class TestConfiguration implements AsyncConfigurer {
|
||||
return new AsyncEventBus(asyncExecutor());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EventBusHolder eventBusHolder() {
|
||||
return EventBusHolder.getInstance();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Executor asyncExecutor() {
|
||||
return new ThreadPoolTaskExecutor();
|
||||
return new DelegatingSecurityContextExecutor(Executors.newSingleThreadExecutor());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -21,5 +21,4 @@ public class CacheKeysTest {
|
||||
final String entitySpecificCacheKey = CacheKeys.entitySpecificCacheKey(knownEntityId, knownCacheKey);
|
||||
assertThat(entitySpecificCacheKey).isEqualTo(knownEntityId + "." + knownCacheKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.rsql;
|
||||
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
|
||||
import org.eclipse.hawkbit.AbstractIntegrationTest;
|
||||
import org.eclipse.hawkbit.TestDataUtil;
|
||||
import org.eclipse.hawkbit.repository.RolloutGroupFields;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupConditionBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
@Features("Component Tests - RSQL filtering")
|
||||
@Stories("RSQL filter rollout group")
|
||||
public class RSQLRolloutGroupFields extends AbstractIntegrationTest {
|
||||
|
||||
private Long rolloutGroupId;
|
||||
private Rollout rollout;
|
||||
|
||||
@Before
|
||||
public void seuptBeforeTest() {
|
||||
final int amountTargets = 20;
|
||||
targetManagement.createTargets(TestDataUtil.buildTargetFixtures(amountTargets, "rollout", "rollout"));
|
||||
final DistributionSet dsA = TestDataUtil.generateDistributionSet("", softwareManagement,
|
||||
distributionSetManagement);
|
||||
rollout = createRollout("rollout1", 4, dsA.getId(), "controllerId==rollout*");
|
||||
rollout = rolloutManagement.findRolloutById(rollout.getId());
|
||||
this.rolloutGroupId = rollout.getRolloutGroups().get(0).getId();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test filter rollout group by id")
|
||||
public void testFilterByParameterId() {
|
||||
assertRSQLQuery(RolloutGroupFields.ID.name() + "==" + rolloutGroupId, 1);
|
||||
assertRSQLQuery(RolloutGroupFields.ID.name() + "==noExist*", 0);
|
||||
assertRSQLQuery(RolloutGroupFields.ID.name() + "=in=(" + rolloutGroupId + ")", 1);
|
||||
assertRSQLQuery(RolloutGroupFields.ID.name() + "=out=(" + rolloutGroupId + ")", 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test filter rollout group by name")
|
||||
public void testFilterByParameterName() {
|
||||
assertRSQLQuery(RolloutGroupFields.NAME.name() + "==group-1", 1);
|
||||
assertRSQLQuery(RolloutGroupFields.NAME.name() + "==*", 4);
|
||||
assertRSQLQuery(RolloutGroupFields.NAME.name() + "==noExist*", 0);
|
||||
assertRSQLQuery(RolloutGroupFields.NAME.name() + "=in=(group-1,group-2)", 2);
|
||||
assertRSQLQuery(RolloutGroupFields.NAME.name() + "=out=(group-1,group-2)", 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test filter rollout group by description")
|
||||
public void testFilterByParameterDescription() {
|
||||
assertRSQLQuery(RolloutGroupFields.DESCRIPTION.name() + "==group-1", 1);
|
||||
assertRSQLQuery(RolloutGroupFields.DESCRIPTION.name() + "==group*", 4);
|
||||
assertRSQLQuery(RolloutGroupFields.DESCRIPTION.name() + "==noExist*", 0);
|
||||
assertRSQLQuery(RolloutGroupFields.DESCRIPTION.name() + "=in=(group-1,notexist)", 1);
|
||||
assertRSQLQuery(RolloutGroupFields.DESCRIPTION.name() + "=out=(group-1,notexist)", 3);
|
||||
}
|
||||
|
||||
private void assertRSQLQuery(final String rsqlParam, final long expcetedTargets) {
|
||||
final Page<RolloutGroup> findTargetPage = rolloutGroupManagement.findRolloutGroupsByPredicate(rollout,
|
||||
RSQLUtility.parse(rsqlParam, RolloutGroupFields.class), new PageRequest(0, 100));
|
||||
final long countTargetsAll = findTargetPage.getTotalElements();
|
||||
assertThat(findTargetPage).isNotNull();
|
||||
assertThat(countTargetsAll).isEqualTo(expcetedTargets);
|
||||
}
|
||||
|
||||
private Rollout createRollout(final String name, final int amountGroups, final long distributionSetId,
|
||||
final String targetFilterQuery) {
|
||||
final Rollout rollout = new Rollout();
|
||||
rollout.setDistributionSet(distributionSetManagement.findDistributionSetById(distributionSetId));
|
||||
rollout.setName(name);
|
||||
rollout.setTargetFilterQuery(targetFilterQuery);
|
||||
return rolloutManagement.createRollout(rollout, amountGroups, new RolloutGroupConditionBuilder()
|
||||
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user