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:
@@ -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