Feature Approval Workflow for rollouts (#678)
* implement feature approval workflow Signed-off-by: Ioannis Spyropoulos <ioannis.spyropoulos@bosch-si.com> Signed-off-by: Michael Müller <Michael.Mueller17@bosch-si.com> * add documentation for REST endpoints Signed-off-by: Ioannis Spyropoulos <ioannis.spyropoulos@bosch-si.com> Signed-off-by: Michael Müller <Michael.Mueller17@bosch-si.com> * fix broken documentation test Signed-off-by: Ioannis Spyropoulos <ioannis.spyropoulos@bosch-si.com> * fix broken documentation test II Signed-off-by: Ioannis Spyropoulos <ioannis.spyropoulos@bosch-si.com>
This commit is contained in:
committed by
Dominic Schabel
parent
8a14c931c8
commit
cef7c2bbf2
@@ -24,7 +24,12 @@ The following capabilities are currently supported by the _Rollout Management_:
|
|||||||
- Selection of targets as input for the rollout based on _target filter_ functionality.
|
- Selection of targets as input for the rollout based on _target filter_ functionality.
|
||||||
- Selection of a _DistributionSet_.
|
- Selection of a _DistributionSet_.
|
||||||
- Auto-splitting of the input target list into a defined number deployment groups.
|
- Auto-splitting of the input target list into a defined number deployment groups.
|
||||||
|
- Approval workflow
|
||||||
|
- Has to be enabled explicitly in configuration.
|
||||||
|
- Enables a workflow that requires a user with adequate permissions to review any new or updated rollout before it
|
||||||
|
can be started.
|
||||||
|
- Allows integration with 3rd party workflow engines.
|
||||||
|
|
||||||
- Cascading start of the deployment groups based on installation status of the previous group.
|
- Cascading start of the deployment groups based on installation status of the previous group.
|
||||||
- Emergency shutdown of the rollout in case a group exceeds the defined error threshold.
|
- Emergency shutdown of the rollout in case a group exceeds the defined error threshold.
|
||||||
- Rollout progress monitoring for the entire rollout and the individual groups.
|
- Rollout progress monitoring for the entire rollout and the individual groups.
|
||||||
|
|||||||
@@ -340,6 +340,54 @@ public interface RolloutManagement {
|
|||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_HANDLE)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_HANDLE)
|
||||||
void resumeRollout(long rolloutId);
|
void resumeRollout(long rolloutId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approves or denies a created rollout being in state
|
||||||
|
* {@link RolloutStatus#WAITING_FOR_APPROVAL}. If the rollout is approved, it
|
||||||
|
* switches state to {@link RolloutStatus#READY}, otherwise it switches to state
|
||||||
|
* {@link RolloutStatus#APPROVAL_DENIED}
|
||||||
|
*
|
||||||
|
* @param rolloutId
|
||||||
|
* the rollout to be approved or denied.
|
||||||
|
* @param decision
|
||||||
|
* decision whether a rollout is approved or denied.
|
||||||
|
*
|
||||||
|
* @return approved or denied rollout
|
||||||
|
*
|
||||||
|
* @throws EntityNotFoundException
|
||||||
|
* if rollout with given ID does not exist
|
||||||
|
* @throws RolloutIllegalStateException
|
||||||
|
* if given rollout is not in
|
||||||
|
* {@link RolloutStatus#WAITING_FOR_APPROVAL}. Only rollouts
|
||||||
|
* waiting for approval can be acted upon.
|
||||||
|
*/
|
||||||
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_APPROVE)
|
||||||
|
Rollout approveOrDeny(long rolloutId, Rollout.ApprovalDecision decision);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approves or denies a created rollout being in state
|
||||||
|
* {@link RolloutStatus#WAITING_FOR_APPROVAL}. If the rollout is approved, it
|
||||||
|
* switches state to {@link RolloutStatus#READY}, otherwise it switches to state
|
||||||
|
* {@link RolloutStatus#APPROVAL_DENIED}
|
||||||
|
*
|
||||||
|
* @param rolloutId
|
||||||
|
* the rollout to be approved or denied.
|
||||||
|
* @param decision
|
||||||
|
* decision whether a rollout is approved or denied.
|
||||||
|
* @param remark
|
||||||
|
* user remark on approve / deny decision
|
||||||
|
*
|
||||||
|
* @return approved or denied rollout
|
||||||
|
*
|
||||||
|
* @throws EntityNotFoundException
|
||||||
|
* if rollout with given ID does not exist
|
||||||
|
* @throws RolloutIllegalStateException
|
||||||
|
* if given rollout is not in
|
||||||
|
* {@link RolloutStatus#WAITING_FOR_APPROVAL}. Only rollouts
|
||||||
|
* waiting for approveOrDeny can be acted upon.
|
||||||
|
*/
|
||||||
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_APPROVE)
|
||||||
|
Rollout approveOrDeny(long rolloutId, Rollout.ApprovalDecision decision, String remark);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a rollout which has been created. The rollout must be in
|
* Starts a rollout which has been created. The rollout must be in
|
||||||
* {@link RolloutStatus#READY} state. The Rollout will be set into the
|
* {@link RolloutStatus#READY} state. The Rollout will be set into the
|
||||||
|
|||||||
@@ -25,6 +25,17 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus.Status;
|
|||||||
*/
|
*/
|
||||||
public interface Rollout extends NamedEntity {
|
public interface Rollout extends NamedEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum length of author name.
|
||||||
|
*/
|
||||||
|
int APPROVED_BY_MAX_SIZE = 40;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum length on comment regarding approval decision.
|
||||||
|
*/
|
||||||
|
int APPROVAL_REMARK_MAX_SIZE = 255;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return <code>true</code> if the rollout is deleted and only kept for
|
* @return <code>true</code> if the rollout is deleted and only kept for
|
||||||
* history purposes.
|
* history purposes.
|
||||||
@@ -81,6 +92,16 @@ public interface Rollout extends NamedEntity {
|
|||||||
*/
|
*/
|
||||||
TotalTargetCountStatus getTotalTargetCountStatus();
|
TotalTargetCountStatus getTotalTargetCountStatus();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return user that approved or denied the {@link Rollout}.
|
||||||
|
*/
|
||||||
|
String getApprovalDecidedBy();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return additional note on approval/denial decision.
|
||||||
|
*/
|
||||||
|
String getApprovalRemark();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* State machine for rollout.
|
* State machine for rollout.
|
||||||
@@ -93,6 +114,16 @@ public interface Rollout extends NamedEntity {
|
|||||||
*/
|
*/
|
||||||
CREATING,
|
CREATING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollout needs to be approved.
|
||||||
|
*/
|
||||||
|
WAITING_FOR_APPROVAL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollout approval is denied. Can not be started.
|
||||||
|
*/
|
||||||
|
APPROVAL_DENIED,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rollout is ready to start.
|
* Rollout is ready to start.
|
||||||
*/
|
*/
|
||||||
@@ -154,4 +185,18 @@ public interface Rollout extends NamedEntity {
|
|||||||
ERROR_STARTING;
|
ERROR_STARTING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum that holds all possible approval workflow decisions.
|
||||||
|
*/
|
||||||
|
enum ApprovalDecision {
|
||||||
|
/**
|
||||||
|
* Representing an granted approval for a rollout.
|
||||||
|
*/
|
||||||
|
APPROVED,
|
||||||
|
/**
|
||||||
|
* Representing a rejected rollout.
|
||||||
|
*/
|
||||||
|
DENIED
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,11 @@ public class TenantConfigurationProperties {
|
|||||||
*/
|
*/
|
||||||
public static final String ANONYMOUS_DOWNLOAD_MODE_ENABLED = "anonymous.download.enabled";
|
public static final String ANONYMOUS_DOWNLOAD_MODE_ENABLED = "anonymous.download.enabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents setting if approval for a rollout is needed.
|
||||||
|
*/
|
||||||
|
public static final String ROLLOUT_APPROVAL_ENABLED = "rollout.approval.enabled";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository on autoclose mode instead of canceling in case of new DS
|
* Repository on autoclose mode instead of canceling in case of new DS
|
||||||
* assignment over active actions.
|
* assignment over active actions.
|
||||||
|
|||||||
@@ -59,12 +59,14 @@ public abstract class AbstractRolloutManagement implements RolloutManagement {
|
|||||||
|
|
||||||
protected final LockRegistry lockRegistry;
|
protected final LockRegistry lockRegistry;
|
||||||
|
|
||||||
|
protected final RolloutApprovalStrategy rolloutApprovalStrategy;
|
||||||
|
|
||||||
protected AbstractRolloutManagement(final TargetManagement targetManagement,
|
protected AbstractRolloutManagement(final TargetManagement targetManagement,
|
||||||
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
|
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
|
||||||
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
|
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
|
||||||
final ApplicationEventPublisher eventPublisher, final VirtualPropertyReplacer virtualPropertyReplacer,
|
final ApplicationEventPublisher eventPublisher, final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||||
final PlatformTransactionManager txManager, final TenantAware tenantAware,
|
final PlatformTransactionManager txManager, final TenantAware tenantAware,
|
||||||
final LockRegistry lockRegistry) {
|
final LockRegistry lockRegistry, final RolloutApprovalStrategy rolloutApprovalStrategy) {
|
||||||
this.targetManagement = targetManagement;
|
this.targetManagement = targetManagement;
|
||||||
this.deploymentManagement = deploymentManagement;
|
this.deploymentManagement = deploymentManagement;
|
||||||
this.rolloutGroupManagement = rolloutGroupManagement;
|
this.rolloutGroupManagement = rolloutGroupManagement;
|
||||||
@@ -75,6 +77,7 @@ public abstract class AbstractRolloutManagement implements RolloutManagement {
|
|||||||
this.txManager = txManager;
|
this.txManager = txManager;
|
||||||
this.tenantAware = tenantAware;
|
this.tenantAware = tenantAware;
|
||||||
this.lockRegistry = lockRegistry;
|
this.lockRegistry = lockRegistry;
|
||||||
|
this.rolloutApprovalStrategy = rolloutApprovalStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Long runInNewTransaction(final String transactionName, final TransactionCallback<Long> action) {
|
protected Long runInNewTransaction(final String transactionName, final TransactionCallback<Long> action) {
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2018 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.Rollout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface provides methods to enable plugging in of different strategies
|
||||||
|
* to handle rollout approval.
|
||||||
|
*/
|
||||||
|
public interface RolloutApprovalStrategy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method handles whether a rollout needs approval. Various factors may be
|
||||||
|
* important according to the implementation, e.g. user roles of the rollout
|
||||||
|
* creator, state of the system, ....
|
||||||
|
*
|
||||||
|
* @param rollout
|
||||||
|
* rollout to decide for whether approval is needed.
|
||||||
|
* @return true if the rollout according to this strategy needs approval.
|
||||||
|
*/
|
||||||
|
boolean isApprovalNeeded(Rollout rollout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Depending on the implementation, creation of a approval task,
|
||||||
|
* notification,... inside or outside of hawkbit may be necessary.
|
||||||
|
* Implementations may also decide to provide an empty implementation for this
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* @param rollout
|
||||||
|
* rollout to create approval task for.
|
||||||
|
*/
|
||||||
|
void onApprovalRequired(Rollout rollout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user that made a decision to approve or deny the given rollout.
|
||||||
|
* Depending on the implementation this may be different to the current user eg.
|
||||||
|
* when the decision is made in an external system.
|
||||||
|
*
|
||||||
|
* @param rollout
|
||||||
|
* target rollout
|
||||||
|
* @return identifier of the user that decided on approval
|
||||||
|
*/
|
||||||
|
String getApprovalUser(Rollout rollout);
|
||||||
|
}
|
||||||
@@ -71,4 +71,10 @@ hawkbit.server.tenant.configuration.anonymous-download-enabled.keyName=anonymous
|
|||||||
hawkbit.server.tenant.configuration.anonymous-download-enabled.defaultValue=${hawkbit.server.download.anonymous.enabled}
|
hawkbit.server.tenant.configuration.anonymous-download-enabled.defaultValue=${hawkbit.server.download.anonymous.enabled}
|
||||||
hawkbit.server.tenant.configuration.anonymous-download-enabled.dataType=java.lang.Boolean
|
hawkbit.server.tenant.configuration.anonymous-download-enabled.dataType=java.lang.Boolean
|
||||||
hawkbit.server.tenant.configuration.anonymous-download-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
|
hawkbit.server.tenant.configuration.anonymous-download-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
|
||||||
|
|
||||||
|
hawkbit.server.tenant.configuration.rollout-approval-enabled.keyName=rollout.approval.enabled
|
||||||
|
hawkbit.server.tenant.configuration.rollout-approval-enabled.defaultValue=false
|
||||||
|
hawkbit.server.tenant.configuration.rollout-approval-enabled.dataType=java.lang.Boolean
|
||||||
|
hawkbit.server.tenant.configuration.rollout-approval-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
|
||||||
|
|
||||||
# Default tenant configuration - END
|
# Default tenant configuration - END
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2018 Bosch Software Innovations GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.repository.jpa;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||||
|
import org.eclipse.hawkbit.im.authentication.UserPrincipal;
|
||||||
|
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
|
||||||
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
|
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of {@link RolloutApprovalStrategy}. Decides whether
|
||||||
|
* approval is needed based on configuration of the tenant as well as the roles
|
||||||
|
* of the user who created the Rollout. Provides a no-operation implementation
|
||||||
|
* of {@link RolloutApprovalStrategy#onApprovalRequired(Rollout)}.
|
||||||
|
*/
|
||||||
|
public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
|
||||||
|
|
||||||
|
private final UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||||
|
|
||||||
|
private final SystemSecurityContext systemSecurityContext;
|
||||||
|
|
||||||
|
DefaultRolloutApprovalStrategy(UserDetailsService userDetailsService,
|
||||||
|
TenantConfigurationManagement tenantConfigurationManagement,
|
||||||
|
SystemSecurityContext systemSecurityContext) {
|
||||||
|
this.userDetailsService = userDetailsService;
|
||||||
|
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||||
|
this.systemSecurityContext = systemSecurityContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true, if rollout approval is enabled and rollout creator doesn't have
|
||||||
|
* approval role.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isApprovalNeeded(final Rollout rollout) {
|
||||||
|
final UserDetails userDetails = this.getActor(rollout);
|
||||||
|
final boolean approvalEnabled = this.tenantConfigurationManagement
|
||||||
|
.getConfigurationValue(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue();
|
||||||
|
return approvalEnabled && userDetails.getAuthorities().stream()
|
||||||
|
.noneMatch(authority -> SpPermission.APPROVE_ROLLOUT.equals(authority.getAuthority()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private UserDetails getActor(Rollout rollout) {
|
||||||
|
final String actor = rollout.getLastModifiedBy() != null ? rollout.getLastModifiedBy() : rollout.getCreatedBy();
|
||||||
|
return systemSecurityContext.runAsSystem(() -> {
|
||||||
|
UserPrincipal userPrincipal = (UserPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
|
if(userPrincipal.getUsername().equals(actor)) {
|
||||||
|
return userPrincipal;
|
||||||
|
} else {
|
||||||
|
return this.userDetailsService.loadUserByUsername(actor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Per default do nothing.
|
||||||
|
*
|
||||||
|
* @param rollout
|
||||||
|
* rollout to create approval task for.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onApprovalRequired(Rollout rollout) {
|
||||||
|
// do nothing per default, can be extended by further implementations.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getApprovalUser(Rollout rollout) {
|
||||||
|
return SecurityContextHolder.getContext().getAuthentication().getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import org.eclipse.hawkbit.repository.AbstractRolloutManagement;
|
|||||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
|
||||||
import org.eclipse.hawkbit.repository.RolloutFields;
|
import org.eclipse.hawkbit.repository.RolloutFields;
|
||||||
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
||||||
import org.eclipse.hawkbit.repository.RolloutHelper;
|
import org.eclipse.hawkbit.repository.RolloutHelper;
|
||||||
@@ -157,9 +158,9 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
|
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
|
||||||
final ApplicationEventPublisher eventPublisher, final VirtualPropertyReplacer virtualPropertyReplacer,
|
final ApplicationEventPublisher eventPublisher, final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||||
final PlatformTransactionManager txManager, final TenantAware tenantAware, final LockRegistry lockRegistry,
|
final PlatformTransactionManager txManager, final TenantAware tenantAware, final LockRegistry lockRegistry,
|
||||||
final Database database) {
|
final Database database, final RolloutApprovalStrategy rolloutApprovalStrategy) {
|
||||||
super(targetManagement, deploymentManagement, rolloutGroupManagement, distributionSetManagement, context,
|
super(targetManagement, deploymentManagement, rolloutGroupManagement, distributionSetManagement, context,
|
||||||
eventPublisher, virtualPropertyReplacer, txManager, tenantAware, lockRegistry);
|
eventPublisher, virtualPropertyReplacer, txManager, tenantAware, lockRegistry, rolloutApprovalStrategy);
|
||||||
this.database = database;
|
this.database = database;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,8 +358,14 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
// When all groups are ready the rollout status can be changed to be
|
// When all groups are ready the rollout status can be changed to be
|
||||||
// ready, too.
|
// ready, too.
|
||||||
if (readyGroups == rolloutGroups.size()) {
|
if (readyGroups == rolloutGroups.size()) {
|
||||||
LOGGER.debug("rollout {} creation done. Switch to READY.", rollout.getId());
|
if (!rolloutApprovalStrategy.isApprovalNeeded(rollout)) {
|
||||||
rollout.setStatus(RolloutStatus.READY);
|
rollout.setStatus(RolloutStatus.READY);
|
||||||
|
LOGGER.debug("rollout {} creation done. Switch to READY.", rollout.getId());
|
||||||
|
} else {
|
||||||
|
LOGGER.debug("rollout {} creation done. Switch to WAITING_FOR_APPROVAL.", rollout.getId());
|
||||||
|
rollout.setStatus(RolloutStatus.WAITING_FOR_APPROVAL);
|
||||||
|
rolloutApprovalStrategy.onApprovalRequired(rollout);
|
||||||
|
}
|
||||||
rollout.setLastCheck(0);
|
rollout.setLastCheck(0);
|
||||||
rollout.setTotalTargets(totalTargets);
|
rollout.setTotalTargets(totalTargets);
|
||||||
rolloutRepository.save(rollout);
|
rolloutRepository.save(rollout);
|
||||||
@@ -451,6 +458,39 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()), baseFilter, totalTargets));
|
groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()), baseFilter, totalTargets));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
@Retryable(include = {
|
||||||
|
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
|
public Rollout approveOrDeny(final long rolloutId, final Rollout.ApprovalDecision decision) {
|
||||||
|
return this.approveOrDeny(rolloutId, decision, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
@Retryable(include = {
|
||||||
|
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
|
public Rollout approveOrDeny(final long rolloutId, final Rollout.ApprovalDecision decision, final String remark) {
|
||||||
|
LOGGER.debug("approveOrDeny rollout called for rollout {} with decision {}", rolloutId, decision);
|
||||||
|
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
|
||||||
|
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.WAITING_FOR_APPROVAL);
|
||||||
|
switch (decision) {
|
||||||
|
case APPROVED:
|
||||||
|
rollout.setStatus(RolloutStatus.READY);
|
||||||
|
break;
|
||||||
|
case DENIED:
|
||||||
|
rollout.setStatus(RolloutStatus.APPROVAL_DENIED);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unknown approval decision: " + decision);
|
||||||
|
}
|
||||||
|
rollout.setApprovalDecidedBy(rolloutApprovalStrategy.getApprovalUser(rollout));
|
||||||
|
if (remark != null) {
|
||||||
|
rollout.setApprovalRemark(remark);
|
||||||
|
}
|
||||||
|
return rolloutRepository.save(rollout);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(include = {
|
@Retryable(include = {
|
||||||
@@ -964,7 +1004,6 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(update.getId());
|
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(update.getId());
|
||||||
|
|
||||||
checkIfDeleted(update.getId(), rollout.getStatus());
|
checkIfDeleted(update.getId(), rollout.getStatus());
|
||||||
|
|
||||||
update.getName().ifPresent(rollout::setName);
|
update.getName().ifPresent(rollout::setName);
|
||||||
update.getDescription().ifPresent(rollout::setDescription);
|
update.getDescription().ifPresent(rollout::setDescription);
|
||||||
update.getActionType().ifPresent(rollout::setActionType);
|
update.getActionType().ifPresent(rollout::setActionType);
|
||||||
@@ -976,6 +1015,11 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
|
|
||||||
rollout.setDistributionSet(set);
|
rollout.setDistributionSet(set);
|
||||||
});
|
});
|
||||||
|
if (rolloutApprovalStrategy.isApprovalNeeded(rollout)) {
|
||||||
|
rollout.setStatus(RolloutStatus.WAITING_FOR_APPROVAL);
|
||||||
|
rollout.setApprovalDecidedBy(null);
|
||||||
|
rollout.setApprovalRemark(null);
|
||||||
|
}
|
||||||
|
|
||||||
return rolloutRepository.save(rollout);
|
return rolloutRepository.save(rollout);
|
||||||
}
|
}
|
||||||
@@ -1067,7 +1111,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
/**
|
/**
|
||||||
* Enforces the quota defining the maximum number of {@link Target}s per
|
* Enforces the quota defining the maximum number of {@link Target}s per
|
||||||
* {@link RolloutGroup}.
|
* {@link RolloutGroup}.
|
||||||
*
|
*
|
||||||
* @param group
|
* @param group
|
||||||
* The rollout group
|
* The rollout group
|
||||||
* @param requested
|
* @param requested
|
||||||
@@ -1081,7 +1125,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
/**
|
/**
|
||||||
* Enforces the quota defining the maximum number of {@link Action}s per
|
* Enforces the quota defining the maximum number of {@link Action}s per
|
||||||
* {@link Target}.
|
* {@link Target}.
|
||||||
*
|
*
|
||||||
* @param target
|
* @param target
|
||||||
* The target
|
* The target
|
||||||
* @param requested
|
* @param requested
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.eclipse.hawkbit.repository.PropertiesQuotaManagement;
|
|||||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryDefaultConfiguration;
|
import org.eclipse.hawkbit.repository.RepositoryDefaultConfiguration;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||||
|
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
|
||||||
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
||||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||||
import org.eclipse.hawkbit.repository.RolloutStatusCache;
|
import org.eclipse.hawkbit.repository.RolloutStatusCache;
|
||||||
@@ -106,6 +107,7 @@ import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
|
|||||||
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
|
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
|
||||||
import org.springframework.retry.annotation.EnableRetry;
|
import org.springframework.retry.annotation.EnableRetry;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||||
@@ -471,7 +473,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
* to access quotas
|
* to access quotas
|
||||||
* @param properties
|
* @param properties
|
||||||
* JPA properties
|
* JPA properties
|
||||||
*
|
*
|
||||||
* @return a new {@link TargetFilterQueryManagement}
|
* @return a new {@link TargetFilterQueryManagement}
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@@ -559,10 +561,25 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
|
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
|
||||||
final ApplicationEventPublisher eventPublisher, final VirtualPropertyReplacer virtualPropertyReplacer,
|
final ApplicationEventPublisher eventPublisher, final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||||
final PlatformTransactionManager txManager, final TenantAware tenantAware, final LockRegistry lockRegistry,
|
final PlatformTransactionManager txManager, final TenantAware tenantAware, final LockRegistry lockRegistry,
|
||||||
final JpaProperties properties) {
|
final JpaProperties properties, final RolloutApprovalStrategy rolloutApprovalStrategy) {
|
||||||
return new JpaRolloutManagement(targetManagement, deploymentManagement, rolloutGroupManagement,
|
return new JpaRolloutManagement(targetManagement, deploymentManagement, rolloutGroupManagement,
|
||||||
distributionSetManagement, context, eventPublisher, virtualPropertyReplacer, txManager, tenantAware,
|
distributionSetManagement, context, eventPublisher, virtualPropertyReplacer, txManager, tenantAware,
|
||||||
lockRegistry, properties.getDatabase());
|
lockRegistry, properties.getDatabase(), rolloutApprovalStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link DefaultRolloutApprovalStrategy} bean.
|
||||||
|
*
|
||||||
|
* @return a new {@link RolloutApprovalStrategy}
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
RolloutApprovalStrategy rolloutApprovalStrategy(final UserDetailsService userDetailsService,
|
||||||
|
final TenantConfigurationManagement tenantConfigurationManagement,
|
||||||
|
final SystemSecurityContext systemSecurityContext) {
|
||||||
|
return new DefaultRolloutApprovalStrategy(userDetailsService, tenantConfigurationManagement,
|
||||||
|
systemSecurityContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -88,7 +88,9 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
|||||||
@ConversionValue(objectValue = "ERROR_CREATING", dataValue = "7"),
|
@ConversionValue(objectValue = "ERROR_CREATING", dataValue = "7"),
|
||||||
@ConversionValue(objectValue = "ERROR_STARTING", dataValue = "8"),
|
@ConversionValue(objectValue = "ERROR_STARTING", dataValue = "8"),
|
||||||
@ConversionValue(objectValue = "DELETING", dataValue = "9"),
|
@ConversionValue(objectValue = "DELETING", dataValue = "9"),
|
||||||
@ConversionValue(objectValue = "DELETED", dataValue = "10") })
|
@ConversionValue(objectValue = "DELETED", dataValue = "10"),
|
||||||
|
@ConversionValue(objectValue = "WAITING_FOR_APPROVAL", dataValue = "11"),
|
||||||
|
@ConversionValue(objectValue = "APPROVAL_DENIED", dataValue = "12")})
|
||||||
@Convert("rolloutstatus")
|
@Convert("rolloutstatus")
|
||||||
@NotNull
|
@NotNull
|
||||||
private RolloutStatus status = RolloutStatus.CREATING;
|
private RolloutStatus status = RolloutStatus.CREATING;
|
||||||
@@ -120,6 +122,14 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
|||||||
@Column(name = "start_at")
|
@Column(name = "start_at")
|
||||||
private Long startAt;
|
private Long startAt;
|
||||||
|
|
||||||
|
@Column(name = "approval_decided_by")
|
||||||
|
@Size(min = 1, max = Rollout.APPROVED_BY_MAX_SIZE)
|
||||||
|
private String approvalDecidedBy;
|
||||||
|
|
||||||
|
@Column(name = "approval_remark")
|
||||||
|
@Size(max = Rollout.APPROVAL_REMARK_MAX_SIZE)
|
||||||
|
private String approvalRemark;
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private transient TotalTargetCountStatus totalTargetCountStatus;
|
private transient TotalTargetCountStatus totalTargetCountStatus;
|
||||||
|
|
||||||
@@ -271,4 +281,22 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
|||||||
this.deleted = deleted;
|
this.deleted = deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getApprovalDecidedBy() {
|
||||||
|
return approvalDecidedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApprovalDecidedBy(final String approvalDecidedBy) {
|
||||||
|
this.approvalDecidedBy = approvalDecidedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getApprovalRemark() {
|
||||||
|
return approvalRemark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApprovalRemark(final String approvalRemark) {
|
||||||
|
this.approvalRemark = approvalRemark;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE sp_rollout ADD column approval_decided_by varchar(40);
|
||||||
|
ALTER TABLE sp_rollout ADD column approval_remark varchar(255);
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE sp_rollout ADD column approval_decided_by varchar(40);
|
||||||
|
ALTER TABLE sp_rollout ADD column approval_remark varchar(255);
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE sp_rollout ADD column approval_decided_by varchar(40);
|
||||||
|
ALTER TABLE sp_rollout ADD column approval_remark varchar(255);
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE sp_rollout ADD column approval_decided_by varchar(40);
|
||||||
|
ALTER TABLE sp_rollout ADD column approval_remark varchar(255);
|
||||||
@@ -27,6 +27,7 @@ import org.eclipse.hawkbit.repository.model.Target;
|
|||||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
|
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
|
||||||
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
|
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
|
||||||
|
import org.eclipse.hawkbit.repository.test.util.RolloutTestApprovalStrategy;
|
||||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||||
@@ -95,6 +96,9 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
|
|||||||
@Autowired
|
@Autowired
|
||||||
protected TenantConfigurationProperties tenantConfigurationProperties;
|
protected TenantConfigurationProperties tenantConfigurationProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected RolloutTestApprovalStrategy approvalStrategy;
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
protected List<Action> findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) {
|
protected List<Action> findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) {
|
||||||
return Lists.newArrayList(actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), actionStatus));
|
return Lists.newArrayList(actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), actionStatus));
|
||||||
|
|||||||
@@ -68,12 +68,17 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
|||||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Slice;
|
import org.springframework.data.domain.Slice;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.domain.Sort.Direction;
|
import org.springframework.data.domain.Sort.Direction;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
|
||||||
import ru.yandex.qatools.allure.annotations.Description;
|
import ru.yandex.qatools.allure.annotations.Description;
|
||||||
import ru.yandex.qatools.allure.annotations.Features;
|
import ru.yandex.qatools.allure.annotations.Features;
|
||||||
import ru.yandex.qatools.allure.annotations.Step;
|
import ru.yandex.qatools.allure.annotations.Step;
|
||||||
@@ -87,6 +92,12 @@ import ru.yandex.qatools.allure.annotations.Title;
|
|||||||
@Stories("Rollout Management")
|
@Stories("Rollout Management")
|
||||||
public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
@After
|
||||||
|
public void reset() {
|
||||||
|
this.approvalStrategy.setApprovalNeeded(false);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verifies that a running action with distribution-set (A) is not canceled by a rollout which tries to also assign a distribution-set (A)")
|
@Description("Verifies that a running action with distribution-set (A) is not canceled by a rollout which tries to also assign a distribution-set (A)")
|
||||||
public void rolloutShouldNotCancelRunningActionWithTheSameDistributionSet() {
|
public void rolloutShouldNotCancelRunningActionWithTheSameDistributionSet() {
|
||||||
@@ -1539,6 +1550,59 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Creating a rollout with approval role or approval engine disabled results in the rollout being in " +
|
||||||
|
"READY state.")
|
||||||
|
public void createdRolloutWithApprovalRoleOrApprovalDisabledTransitionsToReadyState() {
|
||||||
|
approvalStrategy.setApprovalNeeded(false);
|
||||||
|
final String successCondition = "50";
|
||||||
|
final String errorCondition = "80";
|
||||||
|
final Rollout rollout = createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10,
|
||||||
|
5, successCondition, errorCondition);
|
||||||
|
assertThat(rollout.getStatus()).isEqualTo(Rollout.RolloutStatus.READY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Creating a rollout without approver role and approval enabled leads to transition to " +
|
||||||
|
"WAITING_FOR_APPROVAL state.")
|
||||||
|
public void createdRolloutWithoutApprovalRoleTransitionsToWaitingForApprovalState() {
|
||||||
|
approvalStrategy.setApprovalNeeded(true);
|
||||||
|
final String successCondition = "50";
|
||||||
|
final String errorCondition = "80";
|
||||||
|
final Rollout rollout = createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10,
|
||||||
|
5, successCondition, errorCondition);
|
||||||
|
assertThat(rollout.getStatus()).isEqualTo(Rollout.RolloutStatus.WAITING_FOR_APPROVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Approving a rollout leads to transition to READY state.")
|
||||||
|
public void approvedRolloutTransitionsToReadyState() {
|
||||||
|
approvalStrategy.setApprovalNeeded(true);
|
||||||
|
final String successCondition = "50";
|
||||||
|
final String errorCondition = "80";
|
||||||
|
final Rollout rollout = createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10,
|
||||||
|
5, successCondition, errorCondition);
|
||||||
|
assertThat(rollout.getStatus()).isEqualTo(Rollout.RolloutStatus.WAITING_FOR_APPROVAL);
|
||||||
|
rolloutManagement.approveOrDeny(rollout.getId(), Rollout.ApprovalDecision.APPROVED);
|
||||||
|
final Rollout resultingRollout = rolloutRepository.findOne(rollout.getId());
|
||||||
|
assertThat(resultingRollout.getStatus()).isEqualTo(Rollout.RolloutStatus.READY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Denying approval for a rollout leads to transition to APPROVAL_DENIED state.")
|
||||||
|
public void deniedRolloutTransitionsToApprovalDeniedState() {
|
||||||
|
approvalStrategy.setApprovalNeeded(true);
|
||||||
|
final String successCondition = "50";
|
||||||
|
final String errorCondition = "80";
|
||||||
|
final Rollout rollout = createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10,
|
||||||
|
5, successCondition, errorCondition);
|
||||||
|
assertThat(rollout.getStatus()).isEqualTo(Rollout.RolloutStatus.WAITING_FOR_APPROVAL);
|
||||||
|
rolloutManagement.approveOrDeny(rollout.getId(), Rollout.ApprovalDecision.DENIED);
|
||||||
|
final Rollout resultingRollout = rolloutRepository.findOne(rollout.getId());
|
||||||
|
assertThat(resultingRollout.getStatus()).isEqualTo(RolloutStatus.APPROVAL_DENIED);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ExpectEvents({ @Expect(type = RolloutDeletedEvent.class, count = 1),
|
@ExpectEvents({ @Expect(type = RolloutDeletedEvent.class, count = 1),
|
||||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
|||||||
@@ -23,11 +23,13 @@ import org.eclipse.hawkbit.cache.DefaultDownloadIdCache;
|
|||||||
import org.eclipse.hawkbit.cache.DownloadIdCache;
|
import org.eclipse.hawkbit.cache.DownloadIdCache;
|
||||||
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
|
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
|
||||||
import org.eclipse.hawkbit.event.BusProtoStuffMessageConverter;
|
import org.eclipse.hawkbit.event.BusProtoStuffMessageConverter;
|
||||||
|
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
|
||||||
import org.eclipse.hawkbit.repository.RolloutStatusCache;
|
import org.eclipse.hawkbit.repository.RolloutStatusCache;
|
||||||
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
|
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
|
||||||
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
||||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
|
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
|
||||||
|
import org.eclipse.hawkbit.repository.test.util.RolloutTestApprovalStrategy;
|
||||||
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
|
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
|
||||||
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
||||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||||
@@ -128,7 +130,6 @@ public class TestConfiguration implements AsyncConfigurer {
|
|||||||
TenantAwareCacheManager cacheManager() {
|
TenantAwareCacheManager cacheManager() {
|
||||||
return new TenantAwareCacheManager(new GuavaCacheManager(), tenantAware());
|
return new TenantAwareCacheManager(new GuavaCacheManager(), tenantAware());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bean for the download id cache.
|
* Bean for the download id cache.
|
||||||
*
|
*
|
||||||
@@ -214,6 +215,11 @@ public class TestConfiguration implements AsyncConfigurer {
|
|||||||
return serviceMatcher;
|
return serviceMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
RolloutApprovalStrategy rolloutApprovalStrategy() {
|
||||||
|
return new RolloutTestApprovalStrategy();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return the protostuff io message converter
|
* @return the protostuff io message converter
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2018 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.test.util;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an approval strategy that can be manipulated by setting the {link
|
||||||
|
* {@link #approvalNeeded}} flag used for testing.
|
||||||
|
*/
|
||||||
|
public class RolloutTestApprovalStrategy implements RolloutApprovalStrategy {
|
||||||
|
|
||||||
|
private boolean approvalNeeded = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isApprovalNeeded(Rollout rollout) {
|
||||||
|
return approvalNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApprovalNeeded(boolean approvalNeeded) {
|
||||||
|
this.approvalNeeded = approvalNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApprovalRequired(Rollout rollout) {
|
||||||
|
// do nothing, as no action is needed when testing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getApprovalUser(Rollout rollout) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,6 +81,36 @@ public interface MgmtRolloutRestApi {
|
|||||||
MediaType.APPLICATION_JSON_VALUE })
|
MediaType.APPLICATION_JSON_VALUE })
|
||||||
ResponseEntity<MgmtRolloutResponseBody> create(MgmtRolloutRestRequestBody rolloutRequestBody);
|
ResponseEntity<MgmtRolloutResponseBody> create(MgmtRolloutRestRequestBody rolloutRequestBody);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the request for approving a rollout.
|
||||||
|
*
|
||||||
|
* @param rolloutId
|
||||||
|
* the ID of the rollout to be approved.
|
||||||
|
* @param remark
|
||||||
|
* an optional remark on the approval decision
|
||||||
|
* @return OK response (200) if rollout is approved now. In case of any
|
||||||
|
* exception the corresponding errors occur.
|
||||||
|
*/
|
||||||
|
@RequestMapping(method = RequestMethod.POST, value = "/{rolloutId}/approve", produces = { MediaTypes.HAL_JSON_VALUE,
|
||||||
|
MediaType.APPLICATION_JSON_VALUE })
|
||||||
|
ResponseEntity<Void> approve(@PathVariable("rolloutId") Long rolloutId,
|
||||||
|
@RequestParam(value = "remark", required = false) String remark);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the request for denying the approval of a rollout.
|
||||||
|
*
|
||||||
|
* @param rolloutId
|
||||||
|
* the ID of the rollout to be denied.
|
||||||
|
* @param remark
|
||||||
|
* an optional remark on the denial decision
|
||||||
|
* @return OK response (200) if rollout is denied now. In case of any
|
||||||
|
* exception the corresponding errors occur.
|
||||||
|
*/
|
||||||
|
@RequestMapping(method = RequestMethod.POST, value = "/{rolloutId}/deny", produces = { MediaTypes.HAL_JSON_VALUE,
|
||||||
|
MediaType.APPLICATION_JSON_VALUE })
|
||||||
|
ResponseEntity<Void> deny(@PathVariable("rolloutId") Long rolloutId,
|
||||||
|
@RequestParam(value = "remark", required = false) String remark);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the POST request for starting a rollout.
|
* Handles the POST request for starting a rollout.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ final class MgmtRolloutMapper {
|
|||||||
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).start(rollout.getId())).withRel("start"));
|
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).start(rollout.getId())).withRel("start"));
|
||||||
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).pause(rollout.getId())).withRel("pause"));
|
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).pause(rollout.getId())).withRel("pause"));
|
||||||
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).resume(rollout.getId())).withRel("resume"));
|
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).resume(rollout.getId())).withRel("resume"));
|
||||||
|
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).approve(rollout.getId(), null)).withRel("approve"));
|
||||||
|
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).deny(rollout.getId(), null)).withRel("deny"));
|
||||||
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).getRolloutGroups(rollout.getId(),
|
body.add(linkTo(methodOn(MgmtRolloutRestApi.class).getRolloutGroups(rollout.getId(),
|
||||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
|
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
|
||||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null)).withRel("groups"));
|
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, null, null)).withRel("groups"));
|
||||||
|
|||||||
@@ -129,6 +129,18 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
|
|||||||
return ResponseEntity.status(HttpStatus.CREATED).body(MgmtRolloutMapper.toResponseRollout(rollout, true));
|
return ResponseEntity.status(HttpStatus.CREATED).body(MgmtRolloutMapper.toResponseRollout(rollout, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<Void> approve(@PathVariable("rolloutId") final Long rolloutId, final String remark) {
|
||||||
|
rolloutManagement.approveOrDeny(rolloutId, Rollout.ApprovalDecision.APPROVED, remark);
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<Void> deny(@PathVariable("rolloutId") final Long rolloutId, final String remark) {
|
||||||
|
rolloutManagement.approveOrDeny(rolloutId, Rollout.ApprovalDecision.DENIED, remark);
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<Void> start(@PathVariable("rolloutId") final Long rolloutId) {
|
public ResponseEntity<Void> start(@PathVariable("rolloutId") final Long rolloutId) {
|
||||||
this.rolloutManagement.start(rolloutId);
|
this.rolloutManagement.start(rolloutId);
|
||||||
|
|||||||
@@ -179,6 +179,81 @@ include::../errors/415.adoc[]
|
|||||||
include::../errors/429.adoc[]
|
include::../errors/429.adoc[]
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
== POST /rest/v1/rollouts/{rolloutId}/approve
|
||||||
|
|
||||||
|
=== Implementation Notes
|
||||||
|
Handles the POST request of approving a created rollout within SP.
|
||||||
|
Only possible if approval workflow is enabled in system configuration and rollout is in state WAITING_FOR_APPROVAL.
|
||||||
|
Required Permission: APPROVE_ROLLOUT
|
||||||
|
|
||||||
|
=== Approve Rollout
|
||||||
|
|
||||||
|
==== CURL
|
||||||
|
|
||||||
|
include::{snippets}/rollouts/approve-rollout/curl-request.adoc[]
|
||||||
|
|
||||||
|
|
||||||
|
==== Request URL
|
||||||
|
|
||||||
|
include::{snippets}/rollouts/approve-rollout/http-request.adoc[]
|
||||||
|
|
||||||
|
|
||||||
|
=== Response (Status 200)
|
||||||
|
|
||||||
|
==== Response example
|
||||||
|
|
||||||
|
include::{snippets}/rollouts/approve-rollout/http-response.adoc[]
|
||||||
|
|
||||||
|
=== Error responses
|
||||||
|
|
||||||
|
|===
|
||||||
|
| HTTP Status Code | Reason | Response Model
|
||||||
|
include::../errors/400.adoc[]
|
||||||
|
include::../errors/401.adoc[]
|
||||||
|
include::../errors/403.adoc[]
|
||||||
|
include::../errors/405.adoc[]
|
||||||
|
include::../errors/406.adoc[]
|
||||||
|
include::../errors/429.adoc[]
|
||||||
|
|===
|
||||||
|
|
||||||
|
== POST /rest/v1/rollouts/{rolloutId}/deny
|
||||||
|
|
||||||
|
=== Implementation Notes
|
||||||
|
Handles the POST request of denying a created rollout within SP.
|
||||||
|
Only possible if approval workflow is enabled in system configuration and rollout is in state WAITING_FOR_APPROVAL.
|
||||||
|
Required Permission: APPROVE_ROLLOUT
|
||||||
|
|
||||||
|
=== Deny Rollout
|
||||||
|
|
||||||
|
|
||||||
|
==== CURL
|
||||||
|
|
||||||
|
include::{snippets}/rollouts/deny-rollout/curl-request.adoc[]
|
||||||
|
|
||||||
|
|
||||||
|
==== Request URL
|
||||||
|
|
||||||
|
include::{snippets}/rollouts/deny-rollout/http-request.adoc[]
|
||||||
|
|
||||||
|
|
||||||
|
=== Response (Status 200)
|
||||||
|
|
||||||
|
==== Response example
|
||||||
|
|
||||||
|
include::{snippets}/rollouts/deny-rollout/http-response.adoc[]
|
||||||
|
|
||||||
|
=== Error responses
|
||||||
|
|
||||||
|
|===
|
||||||
|
| HTTP Status Code | Reason | Response Model
|
||||||
|
include::../errors/400.adoc[]
|
||||||
|
include::../errors/401.adoc[]
|
||||||
|
include::../errors/403.adoc[]
|
||||||
|
include::../errors/405.adoc[]
|
||||||
|
include::../errors/406.adoc[]
|
||||||
|
include::../errors/429.adoc[]
|
||||||
|
|===
|
||||||
|
|
||||||
== POST /rest/v1/rollouts/{rolloutId}/start
|
== POST /rest/v1/rollouts/{rolloutId}/start
|
||||||
|
|
||||||
=== Implementation Notes
|
=== Implementation Notes
|
||||||
|
|||||||
@@ -103,6 +103,8 @@ public final class MgmtApiModelProperties {
|
|||||||
public static final String ROLLOUT_LINKS_START_ASYNC = "Link to start the rollout in async mode";
|
public static final String ROLLOUT_LINKS_START_ASYNC = "Link to start the rollout in async mode";
|
||||||
public static final String ROLLOUT_LINKS_PAUSE = "Link to pause a running rollout";
|
public static final String ROLLOUT_LINKS_PAUSE = "Link to pause a running rollout";
|
||||||
public static final String ROLLOUT_LINKS_RESUME = "Link to resume a paused rollout";
|
public static final String ROLLOUT_LINKS_RESUME = "Link to resume a paused rollout";
|
||||||
|
public static final String ROLLOUT_LINKS_APPROVE = "Link to approve a rollout";
|
||||||
|
public static final String ROLLOUT_LINKS_DENY = "Link to deny a rollout";
|
||||||
public static final String ROLLOUT_LINKS_GROUPS = "Link to retrieve the groups a rollout";
|
public static final String ROLLOUT_LINKS_GROUPS = "Link to retrieve the groups a rollout";
|
||||||
public static final String ROLLOUT_START_ASYNC = "Start the rollout asynchronous";
|
public static final String ROLLOUT_START_ASYNC = "Start the rollout asynchronous";
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessActi
|
|||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
|
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
|
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
|
||||||
|
import org.eclipse.hawkbit.repository.test.util.RolloutTestApprovalStrategy;
|
||||||
import org.eclipse.hawkbit.rest.documentation.AbstractApiRestDocumentation;
|
import org.eclipse.hawkbit.rest.documentation.AbstractApiRestDocumentation;
|
||||||
import org.eclipse.hawkbit.rest.documentation.ApiModelPropertiesGeneric;
|
import org.eclipse.hawkbit.rest.documentation.ApiModelPropertiesGeneric;
|
||||||
import org.eclipse.hawkbit.rest.documentation.DocumenationResponseFieldsSnippet;
|
import org.eclipse.hawkbit.rest.documentation.DocumenationResponseFieldsSnippet;
|
||||||
@@ -41,6 +42,7 @@ import org.eclipse.hawkbit.rest.util.JsonBuilder;
|
|||||||
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.hateoas.MediaTypes;
|
import org.springframework.hateoas.MediaTypes;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -62,12 +64,16 @@ import ru.yandex.qatools.allure.annotations.Stories;
|
|||||||
@Stories("Rollout Resource")
|
@Stories("Rollout Resource")
|
||||||
public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentation {
|
public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentation {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RolloutTestApprovalStrategy approvalStrategy;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
this.resourceName = "rollouts";
|
this.resourceName = "rollouts";
|
||||||
super.setUp();
|
super.setUp();
|
||||||
arrayPrefix = "content[].";
|
arrayPrefix = "content[].";
|
||||||
|
approvalStrategy.setApprovalNeeded(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -127,7 +133,6 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
|
|||||||
allFieldDescriptor.add(
|
allFieldDescriptor.add(
|
||||||
fieldWithPath(arrayPrefix + "totalTargets").description(MgmtApiModelProperties.ROLLOUT_TOTAL_TARGETS));
|
fieldWithPath(arrayPrefix + "totalTargets").description(MgmtApiModelProperties.ROLLOUT_TOTAL_TARGETS));
|
||||||
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.self").ignored());
|
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.self").ignored());
|
||||||
|
|
||||||
if (withDetails) {
|
if (withDetails) {
|
||||||
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "totalTargetsPerStatus")
|
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "totalTargetsPerStatus")
|
||||||
.description(MgmtApiModelProperties.ROLLOUT_TOTAL_TARGETS_PER_STATUS));
|
.description(MgmtApiModelProperties.ROLLOUT_TOTAL_TARGETS_PER_STATUS));
|
||||||
@@ -139,6 +144,10 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
|
|||||||
.description(MgmtApiModelProperties.ROLLOUT_LINKS_RESUME));
|
.description(MgmtApiModelProperties.ROLLOUT_LINKS_RESUME));
|
||||||
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.groups")
|
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.groups")
|
||||||
.description(MgmtApiModelProperties.ROLLOUT_LINKS_GROUPS));
|
.description(MgmtApiModelProperties.ROLLOUT_LINKS_GROUPS));
|
||||||
|
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.approve")
|
||||||
|
.description(MgmtApiModelProperties.ROLLOUT_LINKS_APPROVE));
|
||||||
|
allFieldDescriptor.add(fieldWithPath(arrayPrefix + "_links.deny")
|
||||||
|
.description(MgmtApiModelProperties.ROLLOUT_LINKS_DENY));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DocumenationResponseFieldsSnippet(allFieldDescriptor);
|
return new DocumenationResponseFieldsSnippet(allFieldDescriptor);
|
||||||
@@ -386,6 +395,29 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati
|
|||||||
pathParameters(parameterWithName("rolloutId").description(ApiModelPropertiesGeneric.ITEM_ID))));
|
pathParameters(parameterWithName("rolloutId").description(ApiModelPropertiesGeneric.ITEM_ID))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Handles the POST request of approving a rollout. Required Permission: "
|
||||||
|
+ SpPermission.APPROVE_ROLLOUT)
|
||||||
|
public void approveRollout() throws Exception {
|
||||||
|
approvalStrategy.setApprovalNeeded(true);
|
||||||
|
final Rollout rollout = createRolloutEntity();
|
||||||
|
mockMvc.perform(post(MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING + "/{rolloutId}/approve", rollout.getId())
|
||||||
|
.accept(MediaTypes.HAL_JSON_VALUE)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
|
.andDo(this.document.document(
|
||||||
|
pathParameters(parameterWithName("rolloutId").description(ApiModelPropertiesGeneric.ITEM_ID))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Handles the POST request of denying a rollout. Required Permission: " + SpPermission.APPROVE_ROLLOUT)
|
||||||
|
public void denyRollout() throws Exception {
|
||||||
|
approvalStrategy.setApprovalNeeded(true);
|
||||||
|
final Rollout rollout = createRolloutEntity();
|
||||||
|
mockMvc.perform(post(MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING + "/{rolloutId}/deny", rollout.getId())
|
||||||
|
.accept(MediaTypes.HAL_JSON_VALUE)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
|
.andDo(this.document.document(
|
||||||
|
pathParameters(parameterWithName("rolloutId").description(ApiModelPropertiesGeneric.ITEM_ID))));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Handles the GET request of retrieving the deploy groups of a rollout. Required Permission: "
|
@Description("Handles the GET request of retrieving the deploy groups of a rollout. Required Permission: "
|
||||||
+ SpPermission.READ_ROLLOUT)
|
+ SpPermission.READ_ROLLOUT)
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ public class TenantResourceDocumentationTest extends AbstractApiRestDocumentatio
|
|||||||
"if the anonymous download mode is enabled.");
|
"if the anonymous download mode is enabled.");
|
||||||
CONFIG_ITEM_DESCRIPTIONS.put(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED,
|
CONFIG_ITEM_DESCRIPTIONS.put(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED,
|
||||||
"if autoclose running actions with new Distribution Set assignment is enabled.");
|
"if autoclose running actions with new Distribution Set assignment is enabled.");
|
||||||
|
CONFIG_ITEM_DESCRIPTIONS.put(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED,
|
||||||
|
"if approval mode for Rollout Management is enabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ spring.http.multipart.max-request-size=-1
|
|||||||
# UI help links
|
# UI help links
|
||||||
hawkbit.server.ui.links.documentation.root=https://www.eclipse.org/hawkbit/documentation/overview/introduction.html
|
hawkbit.server.ui.links.documentation.root=https://www.eclipse.org/hawkbit/documentation/overview/introduction.html
|
||||||
hawkbit.server.ui.links.documentation.security=https://www.eclipse.org/hawkbit/documentation/security/security.html
|
hawkbit.server.ui.links.documentation.security=https://www.eclipse.org/hawkbit/documentation/security/security.html
|
||||||
|
hawkbit.server.ui.links.documentation.rollout=https://www.eclipse.org/hawkbit/documentation/architecture/rollout-management.html
|
||||||
|
|
||||||
hawkbit.server.ui.links.documentation.deployment-view=https://www.eclipse.org/hawkbit/documentation/interfaces/management-ui.html
|
hawkbit.server.ui.links.documentation.deployment-view=https://www.eclipse.org/hawkbit/documentation/interfaces/management-ui.html
|
||||||
hawkbit.server.ui.links.documentation.distribution-view=https://www.eclipse.org/hawkbit/documentation/interfaces/management-ui.html
|
hawkbit.server.ui.links.documentation.distribution-view=https://www.eclipse.org/hawkbit/documentation/interfaces/management-ui.html
|
||||||
|
|||||||
@@ -145,6 +145,11 @@ public final class SpPermission {
|
|||||||
*/
|
*/
|
||||||
public static final String HANDLE_ROLLOUT = "HANDLE_ROLLOUT";
|
public static final String HANDLE_ROLLOUT = "HANDLE_ROLLOUT";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission to approve or deny a rollout prior to starting.
|
||||||
|
*/
|
||||||
|
public static final String APPROVE_ROLLOUT = "APPROVE_ROLLOUT";
|
||||||
|
|
||||||
private SpPermission() {
|
private SpPermission() {
|
||||||
// Constants only
|
// Constants only
|
||||||
}
|
}
|
||||||
@@ -389,6 +394,14 @@ public final class SpPermission {
|
|||||||
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_HANDLE = HAS_AUTH_PREFIX + HANDLE_ROLLOUT
|
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_HANDLE = HAS_AUTH_PREFIX + HANDLE_ROLLOUT
|
||||||
+ HAS_AUTH_SUFFIX + HAS_AUTH_OR + IS_SYSTEM_CODE;
|
+ HAS_AUTH_SUFFIX + HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#APPROVE_ROLLOUT} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_APPROVE = HAS_AUTH_PREFIX + APPROVE_ROLLOUT
|
||||||
|
+ HAS_AUTH_SUFFIX + HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
* context contains {@link SpPermission#UPDATE_ROLLOUT} or
|
* context contains {@link SpPermission#UPDATE_ROLLOUT} or
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public final class PermissionTest {
|
|||||||
@Test
|
@Test
|
||||||
@Description("Verify the get permission function")
|
@Description("Verify the get permission function")
|
||||||
public void testGetPermissions() {
|
public void testGetPermissions() {
|
||||||
final int allPermission = 17;
|
final int allPermission = 18;
|
||||||
final Collection<String> allAuthorities = SpPermission.getAllAuthorities();
|
final Collection<String> allAuthorities = SpPermission.getAllAuthorities();
|
||||||
final List<GrantedAuthority> allAuthoritiesList = PermissionUtils.createAllAuthorityList();
|
final List<GrantedAuthority> allAuthoritiesList = PermissionUtils.createAllAuthorityList();
|
||||||
assertThat(allAuthorities).hasSize(allPermission);
|
assertThat(allAuthorities).hasSize(allPermission);
|
||||||
|
|||||||
@@ -151,4 +151,12 @@ public class SpPermissionChecker implements Serializable {
|
|||||||
public boolean hasRolloutTargetsReadPermission() {
|
public boolean hasRolloutTargetsReadPermission() {
|
||||||
return hasTargetReadPermission() && permissionService.hasPermission(SpPermission.READ_ROLLOUT);
|
return hasTargetReadPermission() && permissionService.hasPermission(SpPermission.READ_ROLLOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if rollout can be approved by the user.
|
||||||
|
*/
|
||||||
|
public boolean hasRolloutApprovalPermission() {
|
||||||
|
return hasRolloutReadPermission() && permissionService.hasPermission(SpPermission.APPROVE_ROLLOUT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,6 +137,11 @@ public class UiProperties implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private String security = "";
|
private String security = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link to rollout related documentation.
|
||||||
|
*/
|
||||||
|
private String rollout = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link to target filter view.
|
* Link to target filter view.
|
||||||
*/
|
*/
|
||||||
@@ -167,6 +172,10 @@ public class UiProperties implements Serializable {
|
|||||||
return security;
|
return security;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRollout() {
|
||||||
|
return rollout;
|
||||||
|
}
|
||||||
|
|
||||||
public String getSystemConfigurationView() {
|
public String getSystemConfigurationView() {
|
||||||
return systemConfigurationView;
|
return systemConfigurationView;
|
||||||
}
|
}
|
||||||
@@ -203,6 +212,10 @@ public class UiProperties implements Serializable {
|
|||||||
this.security = security;
|
this.security = security;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRollout(final String rollout) {
|
||||||
|
this.rollout = rollout;
|
||||||
|
}
|
||||||
|
|
||||||
public void setSystemConfigurationView(final String systemConfigurationView) {
|
public void setSystemConfigurationView(final String systemConfigurationView) {
|
||||||
this.systemConfigurationView = systemConfigurationView;
|
this.systemConfigurationView = systemConfigurationView;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
|||||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.eclipse.hawkbit.ui.AbstractHawkbitUI;
|
import org.eclipse.hawkbit.ui.AbstractHawkbitUI;
|
||||||
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
||||||
@@ -69,16 +70,17 @@ public class RolloutView extends VerticalLayout implements View {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
RolloutView(final SpPermissionChecker permissionChecker, final RolloutUIState rolloutUIState,
|
RolloutView(final SpPermissionChecker permissionChecker, final RolloutUIState rolloutUIState,
|
||||||
final UIEventBus eventBus, final RolloutManagement rolloutManagement,
|
final UIEventBus eventBus, final RolloutManagement rolloutManagement,
|
||||||
final RolloutGroupManagement rolloutGroupManagement, final TargetManagement targetManagement,
|
final RolloutGroupManagement rolloutGroupManagement, final TargetManagement targetManagement,
|
||||||
final UINotification uiNotification, final UiProperties uiProperties, final EntityFactory entityFactory,
|
final UINotification uiNotification, final UiProperties uiProperties, final EntityFactory entityFactory,
|
||||||
final VaadinMessageSource i18n, final TargetFilterQueryManagement targetFilterQueryManagement,
|
final VaadinMessageSource i18n, final TargetFilterQueryManagement targetFilterQueryManagement,
|
||||||
final QuotaManagement quotaManagement) {
|
final QuotaManagement quotaManagement,
|
||||||
|
final TenantConfigurationManagement tenantConfigManagement) {
|
||||||
this.permChecker = permissionChecker;
|
this.permChecker = permissionChecker;
|
||||||
this.rolloutManagement = rolloutManagement;
|
this.rolloutManagement = rolloutManagement;
|
||||||
this.rolloutListView = new RolloutListView(permissionChecker, rolloutUIState, eventBus, rolloutManagement,
|
this.rolloutListView = new RolloutListView(permissionChecker, rolloutUIState, eventBus, rolloutManagement,
|
||||||
targetManagement, uiNotification, uiProperties, entityFactory, i18n, targetFilterQueryManagement,
|
targetManagement, uiNotification, uiProperties, entityFactory, i18n, targetFilterQueryManagement,
|
||||||
rolloutGroupManagement, quotaManagement);
|
rolloutGroupManagement, quotaManagement, tenantConfigManagement);
|
||||||
this.rolloutGroupsListView = new RolloutGroupsListView(i18n, eventBus, rolloutGroupManagement, rolloutUIState,
|
this.rolloutGroupsListView = new RolloutGroupsListView(i18n, eventBus, rolloutGroupManagement, rolloutUIState,
|
||||||
permissionChecker);
|
permissionChecker);
|
||||||
this.rolloutGroupTargetsListView = new RolloutGroupTargetsListView(eventBus, i18n, rolloutUIState);
|
this.rolloutGroupTargetsListView = new RolloutGroupTargetsListView(eventBus, i18n, rolloutUIState);
|
||||||
|
|||||||
@@ -78,9 +78,11 @@ import com.vaadin.data.util.converter.StringToIntegerConverter;
|
|||||||
import com.vaadin.data.validator.IntegerRangeValidator;
|
import com.vaadin.data.validator.IntegerRangeValidator;
|
||||||
import com.vaadin.data.validator.LongRangeValidator;
|
import com.vaadin.data.validator.LongRangeValidator;
|
||||||
import com.vaadin.data.validator.NullValidator;
|
import com.vaadin.data.validator.NullValidator;
|
||||||
|
import com.vaadin.server.FontAwesome;
|
||||||
import com.vaadin.shared.ui.label.ContentMode;
|
import com.vaadin.shared.ui.label.ContentMode;
|
||||||
import com.vaadin.ui.ComboBox;
|
import com.vaadin.ui.ComboBox;
|
||||||
import com.vaadin.ui.GridLayout;
|
import com.vaadin.ui.GridLayout;
|
||||||
|
import com.vaadin.ui.HorizontalLayout;
|
||||||
import com.vaadin.ui.Label;
|
import com.vaadin.ui.Label;
|
||||||
import com.vaadin.ui.OptionGroup;
|
import com.vaadin.ui.OptionGroup;
|
||||||
import com.vaadin.ui.TabSheet;
|
import com.vaadin.ui.TabSheet;
|
||||||
@@ -106,6 +108,10 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
|
|
||||||
private static final String MESSAGE_ENTER_NUMBER = "message.enter.number";
|
private static final String MESSAGE_ENTER_NUMBER = "message.enter.number";
|
||||||
|
|
||||||
|
private static final String APPROVAL_BUTTON_LABEL = "button.approve";
|
||||||
|
|
||||||
|
private static final String DENY_BUTTON_LABEL = "button.deny";
|
||||||
|
|
||||||
private final ActionTypeOptionGroupLayout actionTypeOptionGroupLayout;
|
private final ActionTypeOptionGroupLayout actionTypeOptionGroupLayout;
|
||||||
|
|
||||||
private final AutoStartOptionGroupLayout autoStartOptionGroupLayout;
|
private final AutoStartOptionGroupLayout autoStartOptionGroupLayout;
|
||||||
@@ -150,6 +156,10 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
|
|
||||||
private OptionGroup errorThresholdOptionGroup;
|
private OptionGroup errorThresholdOptionGroup;
|
||||||
|
|
||||||
|
private Label approvalLabel;
|
||||||
|
|
||||||
|
private HorizontalLayout approvalButtonsLayout;
|
||||||
|
|
||||||
private CommonDialogWindow window;
|
private CommonDialogWindow window;
|
||||||
|
|
||||||
private boolean editRolloutEnabled;
|
private boolean editRolloutEnabled;
|
||||||
@@ -166,6 +176,10 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
|
|
||||||
private GroupsLegendLayout groupsLegendLayout;
|
private GroupsLegendLayout groupsLegendLayout;
|
||||||
|
|
||||||
|
private OptionGroup approveButtonsGroup;
|
||||||
|
|
||||||
|
private TextField approvalRemarkField;
|
||||||
|
|
||||||
private final transient RolloutGroupConditions defaultRolloutGroupConditions;
|
private final transient RolloutGroupConditions defaultRolloutGroupConditions;
|
||||||
|
|
||||||
private final NullValidator nullValidator = new NullValidator(null, false);
|
private final NullValidator nullValidator = new NullValidator(null, false);
|
||||||
@@ -213,6 +227,11 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
|
|
||||||
if (editRolloutEnabled) {
|
if (editRolloutEnabled) {
|
||||||
editRollout();
|
editRollout();
|
||||||
|
if (rollout.getStatus().equals(Rollout.RolloutStatus.WAITING_FOR_APPROVAL)) {
|
||||||
|
rolloutManagement.approveOrDeny(rollout.getId(),
|
||||||
|
(Rollout.ApprovalDecision) approveButtonsGroup.getValue(), approvalRemarkField.getValue());
|
||||||
|
eventBus.publish(this, RolloutEvent.UPDATE_ROLLOUT);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
createRollout();
|
createRollout();
|
||||||
@@ -413,6 +432,12 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
rollout = null;
|
rollout = null;
|
||||||
groupsDefinitionTabs.setVisible(true);
|
groupsDefinitionTabs.setVisible(true);
|
||||||
groupsDefinitionTabs.setSelectedTab(0);
|
groupsDefinitionTabs.setSelectedTab(0);
|
||||||
|
|
||||||
|
approvalLabel.setVisible(false);
|
||||||
|
approvalButtonsLayout.setVisible(false);
|
||||||
|
approveButtonsGroup.clear();
|
||||||
|
approvalRemarkField.clear();
|
||||||
|
approveButtonsGroup.removeAllValidators();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addGroupsDefinitionTabs() {
|
private void addGroupsDefinitionTabs() {
|
||||||
@@ -443,7 +468,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
|
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
setSizeUndefined();
|
setSizeUndefined();
|
||||||
setRows(7);
|
setRows(8);
|
||||||
setColumns(4);
|
setColumns(4);
|
||||||
setStyleName("marginTop");
|
setStyleName("marginTop");
|
||||||
setColumnExpandRatio(3, 1);
|
setColumnExpandRatio(3, 1);
|
||||||
@@ -477,6 +502,9 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
|
|
||||||
addComponent(groupsDefinitionTabs, 0, 6, 3, 6);
|
addComponent(groupsDefinitionTabs, 0, 6, 3, 6);
|
||||||
|
|
||||||
|
addComponent(approvalLabel, 0, 7);
|
||||||
|
addComponent(approvalButtonsLayout, 1, 7, 3, 7);
|
||||||
|
|
||||||
rolloutName.focus();
|
rolloutName.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,6 +569,8 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
|
|
||||||
groupsLegendLayout = new GroupsLegendLayout(i18n);
|
groupsLegendLayout = new GroupsLegendLayout(i18n);
|
||||||
|
|
||||||
|
approvalLabel = getLabel("label.approval.decision");
|
||||||
|
approvalButtonsLayout = createApprovalLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayValidationStatus(final DefineGroupsLayout.ValidationStatus status) {
|
private void displayValidationStatus(final DefineGroupsLayout.ValidationStatus status) {
|
||||||
@@ -610,6 +640,29 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HorizontalLayout createApprovalLayout() {
|
||||||
|
approveButtonsGroup = new OptionGroup();
|
||||||
|
approveButtonsGroup.setId(UIComponentIdProvider.ROLLOUT_APPROVAL_OPTIONGROUP_ID);
|
||||||
|
approveButtonsGroup.addStyleName(ValoTheme.OPTIONGROUP_SMALL);
|
||||||
|
approveButtonsGroup.addStyleName(ValoTheme.OPTIONGROUP_HORIZONTAL);
|
||||||
|
approveButtonsGroup.addStyleName("custom-option-group");
|
||||||
|
approveButtonsGroup.addItems(Rollout.ApprovalDecision.APPROVED, Rollout.ApprovalDecision.DENIED);
|
||||||
|
|
||||||
|
approveButtonsGroup.setItemCaption(Rollout.ApprovalDecision.APPROVED, i18n.getMessage(APPROVAL_BUTTON_LABEL));
|
||||||
|
approveButtonsGroup.setItemIcon(Rollout.ApprovalDecision.APPROVED, FontAwesome.CHECK);
|
||||||
|
approveButtonsGroup.setItemCaption(Rollout.ApprovalDecision.DENIED, i18n.getMessage(DENY_BUTTON_LABEL));
|
||||||
|
approveButtonsGroup.setItemIcon(Rollout.ApprovalDecision.DENIED, FontAwesome.TIMES);
|
||||||
|
|
||||||
|
approvalRemarkField = createTextField("label.approval.remark",
|
||||||
|
UIComponentIdProvider.ROLLOUT_APPROVAL_REMARK_FIELD_ID, Rollout.APPROVAL_REMARK_MAX_SIZE);
|
||||||
|
approvalRemarkField.setWidth(100.0F, Unit.PERCENTAGE);
|
||||||
|
|
||||||
|
HorizontalLayout layout = new HorizontalLayout(approveButtonsGroup, approvalRemarkField);
|
||||||
|
layout.setWidth(100.0F, Unit.PERCENTAGE);
|
||||||
|
layout.setExpandRatio(approvalRemarkField, 1.0F);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
private static Label createCountLabel() {
|
private static Label createCountLabel() {
|
||||||
final Label groupSize = new LabelBuilder().visible(false).name("").buildLabel();
|
final Label groupSize = new LabelBuilder().visible(false).name("").buildLabel();
|
||||||
groupSize.addStyleName(ValoTheme.LABEL_TINY + " " + "rollout-target-count-message");
|
groupSize.addStyleName(ValoTheme.LABEL_TINY + " " + "rollout-target-count-message");
|
||||||
@@ -966,6 +1019,13 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
if (rollout.getStatus() != Rollout.RolloutStatus.READY) {
|
if (rollout.getStatus() != Rollout.RolloutStatus.READY) {
|
||||||
disableRequiredFieldsOnEdit();
|
disableRequiredFieldsOnEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rollout.getStatus() == Rollout.RolloutStatus.WAITING_FOR_APPROVAL) {
|
||||||
|
approvalButtonsLayout.setVisible(true);
|
||||||
|
approveButtonsGroup.addValidator(nullValidator);
|
||||||
|
approvalLabel.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
rolloutName.setValue(rollout.getName());
|
rolloutName.setValue(rollout.getName());
|
||||||
groupsDefinitionTabs.setVisible(false);
|
groupsDefinitionTabs.setVisible(false);
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ public class ProxyRollout {
|
|||||||
private long forcedTime;
|
private long forcedTime;
|
||||||
private RolloutStatus status;
|
private RolloutStatus status;
|
||||||
private TotalTargetCountStatus totalTargetCountStatus;
|
private TotalTargetCountStatus totalTargetCountStatus;
|
||||||
|
private String approvalDecidedBy;
|
||||||
|
private String approvalRemark;
|
||||||
|
|
||||||
public RolloutRendererData getRolloutRendererData() {
|
public RolloutRendererData getRolloutRendererData() {
|
||||||
return rolloutRendererData;
|
return rolloutRendererData;
|
||||||
@@ -217,4 +219,20 @@ public class ProxyRollout {
|
|||||||
public void setTotalTargetCountStatus(final TotalTargetCountStatus totalTargetCountStatus) {
|
public void setTotalTargetCountStatus(final TotalTargetCountStatus totalTargetCountStatus) {
|
||||||
this.totalTargetCountStatus = totalTargetCountStatus;
|
this.totalTargetCountStatus = totalTargetCountStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getApprovalDecidedBy() {
|
||||||
|
return approvalDecidedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApprovalDecidedBy(final String approvalDecidedBy) {
|
||||||
|
this.approvalDecidedBy = approvalDecidedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApprovalRemark() {
|
||||||
|
return approvalRemark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApprovalRemark(final String approvalRemark) {
|
||||||
|
this.approvalRemark = approvalRemark;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,8 @@ public class RolloutBeanQuery extends AbstractBeanQuery<ProxyRollout> {
|
|||||||
final TotalTargetCountStatus totalTargetCountActionStatus = rollout.getTotalTargetCountStatus();
|
final TotalTargetCountStatus totalTargetCountActionStatus = rollout.getTotalTargetCountStatus();
|
||||||
proxyRollout.setTotalTargetCountStatus(totalTargetCountActionStatus);
|
proxyRollout.setTotalTargetCountStatus(totalTargetCountActionStatus);
|
||||||
proxyRollout.setTotalTargetsCount(String.valueOf(rollout.getTotalTargets()));
|
proxyRollout.setTotalTargetsCount(String.valueOf(rollout.getTotalTargets()));
|
||||||
|
proxyRollout.setApprovalDecidedBy(rollout.getApprovalDecidedBy());
|
||||||
|
proxyRollout.setApprovalRemark(rollout.getApprovalRemark());
|
||||||
return proxyRollout;
|
return proxyRollout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.ui.rollout.rollout;
|
package org.eclipse.hawkbit.ui.rollout.rollout;
|
||||||
|
|
||||||
|
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.*;
|
||||||
import static org.eclipse.hawkbit.ui.rollout.DistributionBarHelper.getTooltip;
|
import static org.eclipse.hawkbit.ui.rollout.DistributionBarHelper.getTooltip;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -24,6 +26,7 @@ import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
|||||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
||||||
@@ -77,6 +80,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
|
|
||||||
private static final String ROLLOUT_RENDERER_DATA = "rolloutRendererData";
|
private static final String ROLLOUT_RENDERER_DATA = "rolloutRendererData";
|
||||||
|
|
||||||
|
private static final String VIRT_PROP_APPROVE = "approve";
|
||||||
private static final String VIRT_PROP_RUN = "run";
|
private static final String VIRT_PROP_RUN = "run";
|
||||||
private static final String VIRT_PROP_PAUSE = "pause";
|
private static final String VIRT_PROP_PAUSE = "pause";
|
||||||
private static final String VIRT_PROP_UPDATE = "update";
|
private static final String VIRT_PROP_UPDATE = "update";
|
||||||
@@ -87,6 +91,8 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
|
|
||||||
private final transient RolloutGroupManagement rolloutGroupManagement;
|
private final transient RolloutGroupManagement rolloutGroupManagement;
|
||||||
|
|
||||||
|
private final transient TenantConfigurationManagement tenantConfigManagement;
|
||||||
|
|
||||||
private final AddUpdateRolloutWindowLayout addUpdateRolloutWindow;
|
private final AddUpdateRolloutWindowLayout addUpdateRolloutWindow;
|
||||||
|
|
||||||
private final UINotification uiNotification;
|
private final UINotification uiNotification;
|
||||||
@@ -95,7 +101,8 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
|
|
||||||
private static final List<RolloutStatus> DELETE_COPY_BUTTON_ENABLED = Arrays.asList(RolloutStatus.CREATING,
|
private static final List<RolloutStatus> DELETE_COPY_BUTTON_ENABLED = Arrays.asList(RolloutStatus.CREATING,
|
||||||
RolloutStatus.ERROR_CREATING, RolloutStatus.ERROR_STARTING, RolloutStatus.PAUSED, RolloutStatus.READY,
|
RolloutStatus.ERROR_CREATING, RolloutStatus.ERROR_STARTING, RolloutStatus.PAUSED, RolloutStatus.READY,
|
||||||
RolloutStatus.RUNNING, RolloutStatus.STARTING, RolloutStatus.STOPPED, RolloutStatus.FINISHED);
|
RolloutStatus.RUNNING, RolloutStatus.STARTING, RolloutStatus.STOPPED, RolloutStatus.FINISHED,
|
||||||
|
RolloutStatus.WAITING_FOR_APPROVAL, RolloutStatus.APPROVAL_DENIED);
|
||||||
|
|
||||||
private static final List<RolloutStatus> UPDATE_BUTTON_ENABLED = Arrays.asList(RolloutStatus.CREATING,
|
private static final List<RolloutStatus> UPDATE_BUTTON_ENABLED = Arrays.asList(RolloutStatus.CREATING,
|
||||||
RolloutStatus.ERROR_CREATING, RolloutStatus.ERROR_STARTING, RolloutStatus.PAUSED, RolloutStatus.READY,
|
RolloutStatus.ERROR_CREATING, RolloutStatus.ERROR_STARTING, RolloutStatus.PAUSED, RolloutStatus.READY,
|
||||||
@@ -106,11 +113,14 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
private static final List<RolloutStatus> RUN_BUTTON_ENABLED = Arrays.asList(RolloutStatus.READY,
|
private static final List<RolloutStatus> RUN_BUTTON_ENABLED = Arrays.asList(RolloutStatus.READY,
|
||||||
RolloutStatus.PAUSED);
|
RolloutStatus.PAUSED);
|
||||||
|
|
||||||
|
private static final List<RolloutStatus> APPROVE_BUTTON_ENABLED = Collections.singletonList(RolloutStatus.WAITING_FOR_APPROVAL);
|
||||||
|
|
||||||
private static final Map<RolloutStatus, StatusFontIcon> statusIconMap = new EnumMap<>(RolloutStatus.class);
|
private static final Map<RolloutStatus, StatusFontIcon> statusIconMap = new EnumMap<>(RolloutStatus.class);
|
||||||
|
|
||||||
private static final List<Object> HIDDEN_COLUMNS = Arrays.asList(SPUILabelDefinitions.VAR_CREATED_DATE,
|
private static final List<Object> HIDDEN_COLUMNS = Arrays.asList(SPUILabelDefinitions.VAR_CREATED_DATE,
|
||||||
SPUILabelDefinitions.VAR_CREATED_USER, SPUILabelDefinitions.VAR_MODIFIED_DATE,
|
SPUILabelDefinitions.VAR_CREATED_USER, SPUILabelDefinitions.VAR_MODIFIED_DATE,
|
||||||
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_DESC);
|
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY,
|
||||||
|
SPUILabelDefinitions.VAR_APPROVAL_REMARK, SPUILabelDefinitions.VAR_DESC);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
statusIconMap.put(RolloutStatus.FINISHED,
|
statusIconMap.put(RolloutStatus.FINISHED,
|
||||||
@@ -118,6 +128,10 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
statusIconMap.put(RolloutStatus.PAUSED,
|
statusIconMap.put(RolloutStatus.PAUSED,
|
||||||
new StatusFontIcon(FontAwesome.PAUSE, SPUIStyleDefinitions.STATUS_ICON_BLUE));
|
new StatusFontIcon(FontAwesome.PAUSE, SPUIStyleDefinitions.STATUS_ICON_BLUE));
|
||||||
statusIconMap.put(RolloutStatus.RUNNING, new StatusFontIcon(null, SPUIStyleDefinitions.STATUS_SPINNER_YELLOW));
|
statusIconMap.put(RolloutStatus.RUNNING, new StatusFontIcon(null, SPUIStyleDefinitions.STATUS_SPINNER_YELLOW));
|
||||||
|
statusIconMap.put(RolloutStatus.WAITING_FOR_APPROVAL,
|
||||||
|
new StatusFontIcon(FontAwesome.HOURGLASS_HALF, SPUIStyleDefinitions.STATUS_ICON_ORANGE));
|
||||||
|
statusIconMap.put(RolloutStatus.APPROVAL_DENIED,
|
||||||
|
new StatusFontIcon(FontAwesome.TIMES_CIRCLE, SPUIStyleDefinitions.STATUS_ICON_RED));
|
||||||
statusIconMap.put(RolloutStatus.READY,
|
statusIconMap.put(RolloutStatus.READY,
|
||||||
new StatusFontIcon(FontAwesome.DOT_CIRCLE_O, SPUIStyleDefinitions.STATUS_ICON_LIGHT_BLUE));
|
new StatusFontIcon(FontAwesome.DOT_CIRCLE_O, SPUIStyleDefinitions.STATUS_ICON_LIGHT_BLUE));
|
||||||
statusIconMap.put(RolloutStatus.STOPPED,
|
statusIconMap.put(RolloutStatus.STOPPED,
|
||||||
@@ -132,14 +146,17 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RolloutListGrid(final VaadinMessageSource i18n, final UIEventBus eventBus,
|
RolloutListGrid(final VaadinMessageSource i18n, final UIEventBus eventBus,
|
||||||
final RolloutManagement rolloutManagement, final UINotification uiNotification,
|
final RolloutManagement rolloutManagement, final UINotification uiNotification,
|
||||||
final RolloutUIState rolloutUIState, final SpPermissionChecker permissionChecker,
|
final RolloutUIState rolloutUIState, final SpPermissionChecker permissionChecker,
|
||||||
final TargetManagement targetManagement, final EntityFactory entityFactory, final UiProperties uiProperties,
|
final TargetManagement targetManagement, final EntityFactory entityFactory,
|
||||||
final TargetFilterQueryManagement targetFilterQueryManagement,
|
final UiProperties uiProperties,
|
||||||
final RolloutGroupManagement rolloutGroupManagement, final QuotaManagement quotaManagement) {
|
final TargetFilterQueryManagement targetFilterQueryManagement,
|
||||||
|
final RolloutGroupManagement rolloutGroupManagement, final QuotaManagement quotaManagement,
|
||||||
|
final TenantConfigurationManagement tenantConfigManagement) {
|
||||||
super(i18n, eventBus, permissionChecker);
|
super(i18n, eventBus, permissionChecker);
|
||||||
this.rolloutManagement = rolloutManagement;
|
this.rolloutManagement = rolloutManagement;
|
||||||
this.rolloutGroupManagement = rolloutGroupManagement;
|
this.rolloutGroupManagement = rolloutGroupManagement;
|
||||||
|
this.tenantConfigManagement = tenantConfigManagement;
|
||||||
this.addUpdateRolloutWindow = new AddUpdateRolloutWindowLayout(rolloutManagement, targetManagement,
|
this.addUpdateRolloutWindow = new AddUpdateRolloutWindowLayout(rolloutManagement, targetManagement,
|
||||||
uiNotification, uiProperties, entityFactory, i18n, eventBus, targetFilterQueryManagement,
|
uiNotification, uiProperties, entityFactory, i18n, eventBus, targetFilterQueryManagement,
|
||||||
rolloutGroupManagement, quotaManagement);
|
rolloutGroupManagement, quotaManagement);
|
||||||
@@ -250,7 +267,10 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
false);
|
false);
|
||||||
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_CREATED_DATE, String.class, null, false,
|
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_CREATED_DATE, String.class, null, false,
|
||||||
false);
|
false);
|
||||||
|
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY, String.class, null, false,
|
||||||
|
false);
|
||||||
|
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_APPROVAL_REMARK, String.class, null, false,
|
||||||
|
false);
|
||||||
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_MODIFIED_DATE, String.class, null, false,
|
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_MODIFIED_DATE, String.class, null, false,
|
||||||
false);
|
false);
|
||||||
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_CREATED_USER, String.class, null, false,
|
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.VAR_CREATED_USER, String.class, null, false,
|
||||||
@@ -289,6 +309,9 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
getColumn(VIRT_PROP_RUN).setMinimumWidth(25);
|
getColumn(VIRT_PROP_RUN).setMinimumWidth(25);
|
||||||
getColumn(VIRT_PROP_RUN).setMaximumWidth(25);
|
getColumn(VIRT_PROP_RUN).setMaximumWidth(25);
|
||||||
|
|
||||||
|
getColumn(VIRT_PROP_APPROVE).setMinimumWidth(25);
|
||||||
|
getColumn(VIRT_PROP_APPROVE).setMaximumWidth(25);
|
||||||
|
|
||||||
getColumn(VIRT_PROP_PAUSE).setMinimumWidth(25);
|
getColumn(VIRT_PROP_PAUSE).setMinimumWidth(25);
|
||||||
getColumn(VIRT_PROP_PAUSE).setMaximumWidth(25);
|
getColumn(VIRT_PROP_PAUSE).setMaximumWidth(25);
|
||||||
|
|
||||||
@@ -315,12 +338,15 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
getColumn(SPUILabelDefinitions.VAR_CREATED_USER).setHeaderCaption(i18n.getMessage("header.createdBy"));
|
getColumn(SPUILabelDefinitions.VAR_CREATED_USER).setHeaderCaption(i18n.getMessage("header.createdBy"));
|
||||||
getColumn(SPUILabelDefinitions.VAR_MODIFIED_DATE).setHeaderCaption(i18n.getMessage("header.modifiedDate"));
|
getColumn(SPUILabelDefinitions.VAR_MODIFIED_DATE).setHeaderCaption(i18n.getMessage("header.modifiedDate"));
|
||||||
getColumn(SPUILabelDefinitions.VAR_MODIFIED_BY).setHeaderCaption(i18n.getMessage("header.modifiedBy"));
|
getColumn(SPUILabelDefinitions.VAR_MODIFIED_BY).setHeaderCaption(i18n.getMessage("header.modifiedBy"));
|
||||||
|
getColumn(SPUILabelDefinitions.VAR_APPROVAL_REMARK).setHeaderCaption(i18n.getMessage("header.approvalRemark"));
|
||||||
|
getColumn(SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY).setHeaderCaption(i18n.getMessage("header.approvalDecidedBy"));
|
||||||
getColumn(SPUILabelDefinitions.VAR_DESC).setHeaderCaption(i18n.getMessage("header.description"));
|
getColumn(SPUILabelDefinitions.VAR_DESC).setHeaderCaption(i18n.getMessage("header.description"));
|
||||||
getColumn(SPUILabelDefinitions.VAR_TOTAL_TARGETS_COUNT_STATUS)
|
getColumn(SPUILabelDefinitions.VAR_TOTAL_TARGETS_COUNT_STATUS)
|
||||||
.setHeaderCaption(i18n.getMessage("header.detail.status"));
|
.setHeaderCaption(i18n.getMessage("header.detail.status"));
|
||||||
getColumn(SPUILabelDefinitions.VAR_STATUS).setHeaderCaption(i18n.getMessage("header.status"));
|
getColumn(SPUILabelDefinitions.VAR_STATUS).setHeaderCaption(i18n.getMessage("header.status"));
|
||||||
|
|
||||||
getColumn(VIRT_PROP_RUN).setHeaderCaption(i18n.getMessage("header.action.run"));
|
getColumn(VIRT_PROP_RUN).setHeaderCaption(i18n.getMessage("header.action.run"));
|
||||||
|
getColumn(VIRT_PROP_APPROVE).setHeaderCaption(i18n.getMessage("header.action.approve"));
|
||||||
getColumn(VIRT_PROP_PAUSE).setHeaderCaption(i18n.getMessage("header.action.pause"));
|
getColumn(VIRT_PROP_PAUSE).setHeaderCaption(i18n.getMessage("header.action.pause"));
|
||||||
getColumn(VIRT_PROP_UPDATE).setHeaderCaption(i18n.getMessage("header.action.update"));
|
getColumn(VIRT_PROP_UPDATE).setHeaderCaption(i18n.getMessage("header.action.update"));
|
||||||
getColumn(VIRT_PROP_COPY).setHeaderCaption(i18n.getMessage("header.action.copy"));
|
getColumn(VIRT_PROP_COPY).setHeaderCaption(i18n.getMessage("header.action.copy"));
|
||||||
@@ -331,7 +357,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
|
|
||||||
private HeaderCell joinColumns() {
|
private HeaderCell joinColumns() {
|
||||||
|
|
||||||
return getDefaultHeaderRow().join(VIRT_PROP_RUN, VIRT_PROP_PAUSE, VIRT_PROP_UPDATE, VIRT_PROP_COPY,
|
return getDefaultHeaderRow().join(VIRT_PROP_RUN, VIRT_PROP_APPROVE, VIRT_PROP_PAUSE, VIRT_PROP_UPDATE, VIRT_PROP_COPY,
|
||||||
VIRT_PROP_DELETE);
|
VIRT_PROP_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,10 +372,11 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
final List<String> columnsToShowInOrder = Arrays.asList(ROLLOUT_RENDERER_DATA,
|
final List<String> columnsToShowInOrder = Arrays.asList(ROLLOUT_RENDERER_DATA,
|
||||||
SPUILabelDefinitions.VAR_DIST_NAME_VERSION, SPUILabelDefinitions.VAR_STATUS,
|
SPUILabelDefinitions.VAR_DIST_NAME_VERSION, SPUILabelDefinitions.VAR_STATUS,
|
||||||
SPUILabelDefinitions.VAR_TOTAL_TARGETS_COUNT_STATUS, SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS,
|
SPUILabelDefinitions.VAR_TOTAL_TARGETS_COUNT_STATUS, SPUILabelDefinitions.VAR_NUMBER_OF_GROUPS,
|
||||||
SPUILabelDefinitions.VAR_TOTAL_TARGETS, VIRT_PROP_RUN, VIRT_PROP_PAUSE, VIRT_PROP_UPDATE,
|
SPUILabelDefinitions.VAR_TOTAL_TARGETS, VIRT_PROP_APPROVE, VIRT_PROP_RUN, VIRT_PROP_PAUSE, VIRT_PROP_UPDATE,
|
||||||
VIRT_PROP_COPY, VIRT_PROP_DELETE, SPUILabelDefinitions.VAR_CREATED_DATE,
|
VIRT_PROP_COPY, VIRT_PROP_DELETE, SPUILabelDefinitions.VAR_CREATED_DATE,
|
||||||
SPUILabelDefinitions.VAR_CREATED_USER, SPUILabelDefinitions.VAR_MODIFIED_DATE,
|
SPUILabelDefinitions.VAR_CREATED_USER, SPUILabelDefinitions.VAR_MODIFIED_DATE,
|
||||||
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_DESC);
|
SPUILabelDefinitions.VAR_MODIFIED_BY, SPUILabelDefinitions.VAR_APPROVAL_DECIDED_BY,
|
||||||
|
SPUILabelDefinitions.VAR_APPROVAL_REMARK, SPUILabelDefinitions.VAR_DESC);
|
||||||
|
|
||||||
setColumns(columnsToShowInOrder.toArray());
|
setColumns(columnsToShowInOrder.toArray());
|
||||||
}
|
}
|
||||||
@@ -361,6 +388,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getColumn(VIRT_PROP_RUN).setHidable(false);
|
getColumn(VIRT_PROP_RUN).setHidable(false);
|
||||||
|
getColumn(VIRT_PROP_APPROVE).setHidable(false);
|
||||||
getColumn(VIRT_PROP_PAUSE).setHidable(false);
|
getColumn(VIRT_PROP_PAUSE).setHidable(false);
|
||||||
getColumn(VIRT_PROP_DELETE).setHidable(false);
|
getColumn(VIRT_PROP_DELETE).setHidable(false);
|
||||||
getColumn(VIRT_PROP_UPDATE).setHidable(false);
|
getColumn(VIRT_PROP_UPDATE).setHidable(false);
|
||||||
@@ -388,6 +416,9 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
getColumn(VIRT_PROP_RUN).setRenderer(
|
getColumn(VIRT_PROP_RUN).setRenderer(
|
||||||
new GridButtonRenderer(clickEvent -> startOrResumeRollout((Long) clickEvent.getItemId())),
|
new GridButtonRenderer(clickEvent -> startOrResumeRollout((Long) clickEvent.getItemId())),
|
||||||
new RolloutGridButtonConverter(this::createRunButtonMetadata));
|
new RolloutGridButtonConverter(this::createRunButtonMetadata));
|
||||||
|
getColumn(VIRT_PROP_APPROVE).setRenderer(
|
||||||
|
new GridButtonRenderer(clickEvent -> approveRollout((Long) clickEvent.getItemId())),
|
||||||
|
new RolloutGridButtonConverter(this::createApprovalButtonMetadata));
|
||||||
getColumn(VIRT_PROP_PAUSE).setRenderer(
|
getColumn(VIRT_PROP_PAUSE).setRenderer(
|
||||||
new GridButtonRenderer(clickEvent -> pauseRollout((Long) clickEvent.getItemId())),
|
new GridButtonRenderer(clickEvent -> pauseRollout((Long) clickEvent.getItemId())),
|
||||||
new RolloutGridButtonConverter(this::createPauseButtonMetadata));
|
new RolloutGridButtonConverter(this::createPauseButtonMetadata));
|
||||||
@@ -440,6 +471,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
final GeneratedPropertyContainer decoratedContainer = getDecoratedContainer();
|
final GeneratedPropertyContainer decoratedContainer = getDecoratedContainer();
|
||||||
|
|
||||||
decoratedContainer.addGeneratedProperty(VIRT_PROP_RUN, new GenericPropertyValueGenerator());
|
decoratedContainer.addGeneratedProperty(VIRT_PROP_RUN, new GenericPropertyValueGenerator());
|
||||||
|
decoratedContainer.addGeneratedProperty(VIRT_PROP_APPROVE, new GenericPropertyValueGenerator());
|
||||||
decoratedContainer.addGeneratedProperty(VIRT_PROP_PAUSE, new GenericPropertyValueGenerator());
|
decoratedContainer.addGeneratedProperty(VIRT_PROP_PAUSE, new GenericPropertyValueGenerator());
|
||||||
decoratedContainer.addGeneratedProperty(VIRT_PROP_UPDATE, new GenericPropertyValueGenerator());
|
decoratedContainer.addGeneratedProperty(VIRT_PROP_UPDATE, new GenericPropertyValueGenerator());
|
||||||
decoratedContainer.addGeneratedProperty(VIRT_PROP_COPY, new GenericPropertyValueGenerator());
|
decoratedContainer.addGeneratedProperty(VIRT_PROP_COPY, new GenericPropertyValueGenerator());
|
||||||
@@ -520,6 +552,13 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void approveRollout(final Long rolloutId) {
|
||||||
|
final CommonDialogWindow addTargetWindow = addUpdateRolloutWindow.getWindow(rolloutId, false);
|
||||||
|
addTargetWindow.setCaption(i18n.getMessage("caption.approve.rollout"));
|
||||||
|
UI.getCurrent().addWindow(addTargetWindow);
|
||||||
|
addTargetWindow.setVisible(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateRollout(final Long rolloutId) {
|
private void updateRollout(final Long rolloutId) {
|
||||||
final CommonDialogWindow addTargetWindow = addUpdateRolloutWindow.getWindow(rolloutId, false);
|
final CommonDialogWindow addTargetWindow = addUpdateRolloutWindow.getWindow(rolloutId, false);
|
||||||
addTargetWindow.setCaption(i18n.getMessage("caption.update.rollout"));
|
addTargetWindow.setCaption(i18n.getMessage("caption.update.rollout"));
|
||||||
@@ -578,7 +617,7 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
String description = null;
|
String description = null;
|
||||||
|
|
||||||
if (SPUILabelDefinitions.VAR_STATUS.equals(cell.getPropertyId())) {
|
if (SPUILabelDefinitions.VAR_STATUS.equals(cell.getPropertyId())) {
|
||||||
description = cell.getProperty().getValue().toString().toLowerCase();
|
description = cell.getProperty().getValue().toString().toLowerCase().replace("_", " ");
|
||||||
} else if (SPUILabelDefinitions.ACTION.equals(cell.getPropertyId())) {
|
} else if (SPUILabelDefinitions.ACTION.equals(cell.getPropertyId())) {
|
||||||
description = SPUILabelDefinitions.ACTION.toLowerCase();
|
description = SPUILabelDefinitions.ACTION.toLowerCase();
|
||||||
} else if (ROLLOUT_RENDERER_DATA.equals(cell.getPropertyId())) {
|
} else if (ROLLOUT_RENDERER_DATA.equals(cell.getPropertyId())) {
|
||||||
@@ -595,6 +634,12 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
return !expectedRolloutStatus.contains(currentRolloutStatus);
|
return !expectedRolloutStatus.contains(currentRolloutStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private StatusFontIcon createApprovalButtonMetadata(final RolloutStatus rolloutStatus) {
|
||||||
|
final boolean isDisabled = hasToBeDisabled(rolloutStatus, APPROVE_BUTTON_ENABLED);
|
||||||
|
return new StatusFontIcon(FontAwesome.GAVEL, null, i18n.getMessage("tooltip.rollout.approve"),
|
||||||
|
UIComponentIdProvider.ROLLOUT_APPROVAL_BUTTON_ID, isDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
private StatusFontIcon createRunButtonMetadata(final RolloutStatus rolloutStatus) {
|
private StatusFontIcon createRunButtonMetadata(final RolloutStatus rolloutStatus) {
|
||||||
final boolean isDisabled = hasToBeDisabled(rolloutStatus, RUN_BUTTON_ENABLED);
|
final boolean isDisabled = hasToBeDisabled(rolloutStatus, RUN_BUTTON_ENABLED);
|
||||||
return new StatusFontIcon(FontAwesome.PLAY, null, i18n.getMessage("tooltip.rollout.run"),
|
return new StatusFontIcon(FontAwesome.PLAY, null, i18n.getMessage("tooltip.rollout.run"),
|
||||||
@@ -744,6 +789,11 @@ public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
|
|||||||
if (!permissionChecker.hasRolloutCreatePermission()) {
|
if (!permissionChecker.hasRolloutCreatePermission()) {
|
||||||
modifiableColumnsList.remove(VIRT_PROP_COPY);
|
modifiableColumnsList.remove(VIRT_PROP_COPY);
|
||||||
}
|
}
|
||||||
|
if (!permissionChecker.hasRolloutApprovalPermission() ||
|
||||||
|
!tenantConfigManagement.getConfigurationValue(
|
||||||
|
TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue()) {
|
||||||
|
modifiableColumnsList.remove(VIRT_PROP_APPROVE);
|
||||||
|
}
|
||||||
if (!permissionChecker.hasRolloutDeletePermission()) {
|
if (!permissionChecker.hasRolloutDeletePermission()) {
|
||||||
modifiableColumnsList.remove(VIRT_PROP_DELETE);
|
modifiableColumnsList.remove(VIRT_PROP_DELETE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
|||||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
||||||
import org.eclipse.hawkbit.ui.UiProperties;
|
import org.eclipse.hawkbit.ui.UiProperties;
|
||||||
import org.eclipse.hawkbit.ui.common.grid.AbstractGrid;
|
import org.eclipse.hawkbit.ui.common.grid.AbstractGrid;
|
||||||
@@ -38,6 +39,7 @@ public class RolloutListView extends AbstractGridComponentLayout {
|
|||||||
private final transient EntityFactory entityFactory;
|
private final transient EntityFactory entityFactory;
|
||||||
private final transient TargetFilterQueryManagement targetFilterQueryManagement;
|
private final transient TargetFilterQueryManagement targetFilterQueryManagement;
|
||||||
private final transient QuotaManagement quotaManagement;
|
private final transient QuotaManagement quotaManagement;
|
||||||
|
private final transient TenantConfigurationManagement tenantConfigManagement;
|
||||||
|
|
||||||
private final SpPermissionChecker permissionChecker;
|
private final SpPermissionChecker permissionChecker;
|
||||||
private final RolloutUIState rolloutUIState;
|
private final RolloutUIState rolloutUIState;
|
||||||
@@ -45,11 +47,13 @@ public class RolloutListView extends AbstractGridComponentLayout {
|
|||||||
private final UiProperties uiProperties;
|
private final UiProperties uiProperties;
|
||||||
|
|
||||||
public RolloutListView(final SpPermissionChecker permissionChecker, final RolloutUIState rolloutUIState,
|
public RolloutListView(final SpPermissionChecker permissionChecker, final RolloutUIState rolloutUIState,
|
||||||
final UIEventBus eventBus, final RolloutManagement rolloutManagement,
|
final UIEventBus eventBus, final RolloutManagement rolloutManagement,
|
||||||
final TargetManagement targetManagement, final UINotification uiNotification,
|
final TargetManagement targetManagement, final UINotification uiNotification,
|
||||||
final UiProperties uiProperties, final EntityFactory entityFactory, final VaadinMessageSource i18n,
|
final UiProperties uiProperties, final EntityFactory entityFactory,
|
||||||
final TargetFilterQueryManagement targetFilterQueryManagement,
|
final VaadinMessageSource i18n,
|
||||||
final RolloutGroupManagement rolloutGroupManagement, final QuotaManagement quotaManagement) {
|
final TargetFilterQueryManagement targetFilterQueryManagement,
|
||||||
|
final RolloutGroupManagement rolloutGroupManagement, final QuotaManagement quotaManagement,
|
||||||
|
final TenantConfigurationManagement tenantConfigManagement) {
|
||||||
super(i18n, eventBus);
|
super(i18n, eventBus);
|
||||||
this.permissionChecker = permissionChecker;
|
this.permissionChecker = permissionChecker;
|
||||||
this.rolloutUIState = rolloutUIState;
|
this.rolloutUIState = rolloutUIState;
|
||||||
@@ -61,6 +65,7 @@ public class RolloutListView extends AbstractGridComponentLayout {
|
|||||||
this.uiProperties = uiProperties;
|
this.uiProperties = uiProperties;
|
||||||
this.entityFactory = entityFactory;
|
this.entityFactory = entityFactory;
|
||||||
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
||||||
|
this.tenantConfigManagement = tenantConfigManagement;
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
@@ -76,7 +81,7 @@ public class RolloutListView extends AbstractGridComponentLayout {
|
|||||||
public AbstractGrid<LazyQueryContainer> createGrid() {
|
public AbstractGrid<LazyQueryContainer> createGrid() {
|
||||||
return new RolloutListGrid(i18n, eventBus, rolloutManagement, uiNotification, rolloutUIState, permissionChecker,
|
return new RolloutListGrid(i18n, eventBus, rolloutManagement, uiNotification, rolloutUIState, permissionChecker,
|
||||||
targetManagement, entityFactory, uiProperties, targetFilterQueryManagement, rolloutGroupManagement,
|
targetManagement, entityFactory, uiProperties, targetFilterQueryManagement, rolloutGroupManagement,
|
||||||
quotaManagement);
|
quotaManagement, tenantConfigManagement);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2018 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.ui.tenantconfiguration;
|
||||||
|
|
||||||
|
import com.vaadin.data.Property;
|
||||||
|
import com.vaadin.ui.CheckBox;
|
||||||
|
import com.vaadin.ui.GridLayout;
|
||||||
|
import com.vaadin.ui.Label;
|
||||||
|
import com.vaadin.ui.Link;
|
||||||
|
import com.vaadin.ui.Panel;
|
||||||
|
import com.vaadin.ui.VerticalLayout;
|
||||||
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
|
import org.eclipse.hawkbit.ui.UiProperties;
|
||||||
|
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
|
||||||
|
import org.eclipse.hawkbit.ui.tenantconfiguration.rollout.ApprovalConfigurationItem;
|
||||||
|
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
|
||||||
|
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides configuration of the RolloutManagement including enabling/disabling
|
||||||
|
* of the approval workflow.
|
||||||
|
*/
|
||||||
|
public class RolloutConfigurationView extends BaseConfigurationView
|
||||||
|
implements Property.ValueChangeListener, ConfigurationItem.ConfigurationItemChangeListener {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final ApprovalConfigurationItem approvalConfigurationItem;
|
||||||
|
private final VaadinMessageSource i18n;
|
||||||
|
private final UiProperties uiProperties;
|
||||||
|
private CheckBox approvalCheckbox;
|
||||||
|
|
||||||
|
RolloutConfigurationView(final VaadinMessageSource i18n,
|
||||||
|
final TenantConfigurationManagement tenantConfigurationManagement, final UiProperties uiProperties) {
|
||||||
|
this.i18n = i18n;
|
||||||
|
this.uiProperties = uiProperties;
|
||||||
|
this.approvalConfigurationItem = new ApprovalConfigurationItem(tenantConfigurationManagement, i18n);
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
|
||||||
|
final Panel rootPanel = new Panel();
|
||||||
|
rootPanel.setSizeFull();
|
||||||
|
|
||||||
|
rootPanel.addStyleName("config-panel");
|
||||||
|
|
||||||
|
final VerticalLayout vLayout = new VerticalLayout();
|
||||||
|
vLayout.setMargin(true);
|
||||||
|
vLayout.setSizeFull();
|
||||||
|
|
||||||
|
final Label headerDisSetType = new Label(i18n.getMessage("configuration.rollout.title"));
|
||||||
|
headerDisSetType.addStyleName("config-panel-header");
|
||||||
|
vLayout.addComponent(headerDisSetType);
|
||||||
|
|
||||||
|
final Link linkToApprovalHelp = SPUIComponentProvider
|
||||||
|
.getHelpLink(uiProperties.getLinks().getDocumentation().getRollout());
|
||||||
|
vLayout.addComponent(linkToApprovalHelp);
|
||||||
|
|
||||||
|
final GridLayout gridLayout = new GridLayout(2, 1);
|
||||||
|
gridLayout.setSpacing(true);
|
||||||
|
gridLayout.setImmediate(true);
|
||||||
|
gridLayout.setColumnExpandRatio(1, 1.0F);
|
||||||
|
|
||||||
|
approvalCheckbox = SPUIComponentProvider.getCheckBox("", "", null, false, "");
|
||||||
|
approvalCheckbox.setId(UIComponentIdProvider.ROLLOUT_APPROVAL_ENABLED_CHECKBOX);
|
||||||
|
approvalCheckbox.setValue(approvalConfigurationItem.isConfigEnabled());
|
||||||
|
approvalCheckbox.addValueChangeListener(this);
|
||||||
|
approvalConfigurationItem.addChangeListener(this);
|
||||||
|
gridLayout.addComponent(approvalCheckbox, 0, 0);
|
||||||
|
gridLayout.addComponent(approvalConfigurationItem, 1, 0);
|
||||||
|
|
||||||
|
vLayout.addComponent(gridLayout);
|
||||||
|
rootPanel.setContent(vLayout);
|
||||||
|
setCompositionRoot(rootPanel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() {
|
||||||
|
this.approvalConfigurationItem.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undo() {
|
||||||
|
this.approvalConfigurationItem.undo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void valueChange(Property.ValueChangeEvent event) {
|
||||||
|
if (approvalCheckbox.equals(event.getProperty())) {
|
||||||
|
if (approvalCheckbox.getValue()) {
|
||||||
|
approvalConfigurationItem.configEnable();
|
||||||
|
} else {
|
||||||
|
approvalConfigurationItem.configDisable();
|
||||||
|
}
|
||||||
|
notifyConfigurationChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configurationHasChanged() {
|
||||||
|
notifyConfigurationChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,6 +62,8 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
|
|||||||
|
|
||||||
private final PollingConfigurationView pollingConfigurationView;
|
private final PollingConfigurationView pollingConfigurationView;
|
||||||
|
|
||||||
|
private final RolloutConfigurationView rolloutConfigurationView;
|
||||||
|
|
||||||
private final VaadinMessageSource i18n;
|
private final VaadinMessageSource i18n;
|
||||||
|
|
||||||
private final UiProperties uiProperties;
|
private final UiProperties uiProperties;
|
||||||
@@ -90,6 +92,7 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
|
|||||||
this.pollingConfigurationView = new PollingConfigurationView(i18n, controllerPollProperties,
|
this.pollingConfigurationView = new PollingConfigurationView(i18n, controllerPollProperties,
|
||||||
tenantConfigurationManagement);
|
tenantConfigurationManagement);
|
||||||
this.repositoryConfigurationView = new RepositoryConfigurationView(i18n, tenantConfigurationManagement);
|
this.repositoryConfigurationView = new RepositoryConfigurationView(i18n, tenantConfigurationManagement);
|
||||||
|
this.rolloutConfigurationView = new RolloutConfigurationView(i18n, tenantConfigurationManagement, uiProperties);
|
||||||
|
|
||||||
this.i18n = i18n;
|
this.i18n = i18n;
|
||||||
this.uiProperties = uiProperties;
|
this.uiProperties = uiProperties;
|
||||||
@@ -105,7 +108,7 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
|
|||||||
configurationViews.add(defaultDistributionSetTypeLayout);
|
configurationViews.add(defaultDistributionSetTypeLayout);
|
||||||
}
|
}
|
||||||
configurationViews.add(repositoryConfigurationView);
|
configurationViews.add(repositoryConfigurationView);
|
||||||
|
configurationViews.add(rolloutConfigurationView);
|
||||||
configurationViews.add(authenticationConfigurationView);
|
configurationViews.add(authenticationConfigurationView);
|
||||||
configurationViews.add(pollingConfigurationView);
|
configurationViews.add(pollingConfigurationView);
|
||||||
if (customConfigurationViews != null) {
|
if (customConfigurationViews != null) {
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2018 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.ui.tenantconfiguration.rollout;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
|
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||||
|
import org.eclipse.hawkbit.ui.tenantconfiguration.generic.AbstractBooleanTenantConfigurationItem;
|
||||||
|
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents the UI item for the target security token section in
|
||||||
|
* the authentication configuration view.
|
||||||
|
*/
|
||||||
|
public class ApprovalConfigurationItem extends AbstractBooleanTenantConfigurationItem {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private boolean configurationEnabled;
|
||||||
|
private boolean configurationEnabledChange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for tenant specific approval mode setting.
|
||||||
|
*
|
||||||
|
* @param tenantConfigurationManagement used to enable/disable the approval mode per tenant
|
||||||
|
* @param i18n used to translate labels
|
||||||
|
*/
|
||||||
|
public ApprovalConfigurationItem(final TenantConfigurationManagement tenantConfigurationManagement,
|
||||||
|
final VaadinMessageSource i18n) {
|
||||||
|
super(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, tenantConfigurationManagement, i18n);
|
||||||
|
|
||||||
|
super.init("configuration.rollout.approval.label");
|
||||||
|
configurationEnabled = isConfigEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configEnable() {
|
||||||
|
if (!configurationEnabled) {
|
||||||
|
configurationEnabledChange = true;
|
||||||
|
}
|
||||||
|
configurationEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configDisable() {
|
||||||
|
if (configurationEnabled) {
|
||||||
|
configurationEnabledChange = true;
|
||||||
|
}
|
||||||
|
configurationEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() {
|
||||||
|
if (!configurationEnabledChange) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getTenantConfigurationManagement().addOrUpdateConfiguration(getConfigurationKey(), configurationEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undo() {
|
||||||
|
configurationEnabledChange = false;
|
||||||
|
configurationEnabled = getTenantConfigurationManagement()
|
||||||
|
.getConfigurationValue(getConfigurationKey(), Boolean.class).getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -133,6 +133,16 @@ public final class SPUILabelDefinitions {
|
|||||||
* Last modified date.
|
* Last modified date.
|
||||||
*/
|
*/
|
||||||
public static final String VAR_MODIFIED_DATE = "modifiedDate";
|
public static final String VAR_MODIFIED_DATE = "modifiedDate";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approve/Deny remark.
|
||||||
|
*/
|
||||||
|
public static final String VAR_APPROVAL_REMARK = "approvalRemark";
|
||||||
|
/**
|
||||||
|
* Approval decider.
|
||||||
|
*/
|
||||||
|
public static final String VAR_APPROVAL_DECIDED_BY = "approvalDecidedBy";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Poll Status.
|
* Poll Status.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -866,6 +866,16 @@ public final class UIComponentIdProvider {
|
|||||||
*/
|
*/
|
||||||
public static final String ROLLOUT_RUN_BUTTON_ID = ROLLOUT_ACTION_ID + ".6";
|
public static final String ROLLOUT_RUN_BUTTON_ID = ROLLOUT_ACTION_ID + ".6";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollout approval button id.
|
||||||
|
*/
|
||||||
|
public static final String ROLLOUT_APPROVAL_BUTTON_ID = ROLLOUT_ACTION_ID + ".11";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollout approve/deny option button group id.
|
||||||
|
*/
|
||||||
|
public static final String ROLLOUT_APPROVAL_OPTIONGROUP_ID = ROLLOUT_ACTION_ID + ".12";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rollout pause button id.
|
* Rollout pause button id.
|
||||||
*/
|
*/
|
||||||
@@ -1082,6 +1092,17 @@ public final class UIComponentIdProvider {
|
|||||||
*/
|
*/
|
||||||
public static final String REPOSITORY_ACTIONS_AUTOCLOSE_CHECKBOX = "repositoryactionsautoclosecheckbox";
|
public static final String REPOSITORY_ACTIONS_AUTOCLOSE_CHECKBOX = "repositoryactionsautoclosecheckbox";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration checkbox for
|
||||||
|
* {@link TenantConfigurationKey#ROLLOUT_APPROVAL_ENABLED}
|
||||||
|
*/
|
||||||
|
public static final String ROLLOUT_APPROVAL_ENABLED_CHECKBOX = "rollout.approve.enabled.checkbox";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Id of the rollout approval remark field
|
||||||
|
*/
|
||||||
|
public static final String ROLLOUT_APPROVAL_REMARK_FIELD_ID = "rollout.approve.remark";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /* Private Constructor.
|
* /* Private Constructor.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ button.cancel = Cancel
|
|||||||
button.upload.file = Upload File
|
button.upload.file = Upload File
|
||||||
button.no.auto.assignment = none
|
button.no.auto.assignment = none
|
||||||
button.auto.assignment.desc = Select auto assign distribution set
|
button.auto.assignment.desc = Select auto assign distribution set
|
||||||
|
button.approve = Approve
|
||||||
|
button.deny = Deny
|
||||||
|
button.apply = Apply
|
||||||
bulk.targets.upload = Please upload csv file.
|
bulk.targets.upload = Please upload csv file.
|
||||||
bulkupload.ds.name = DS Name
|
bulkupload.ds.name = DS Name
|
||||||
button.discard=Discard
|
button.discard=Discard
|
||||||
@@ -72,6 +75,7 @@ header.swmodules=SwModules
|
|||||||
header.migrations.step=IsRequiredMigrationStep
|
header.migrations.step=IsRequiredMigrationStep
|
||||||
header.action=Actions
|
header.action=Actions
|
||||||
header.action.run=Run
|
header.action.run=Run
|
||||||
|
header.action.approve=Approve
|
||||||
header.action.pause=Pause
|
header.action.pause=Pause
|
||||||
header.action.update=Edit
|
header.action.update=Edit
|
||||||
header.action.copy=Copy
|
header.action.copy=Copy
|
||||||
@@ -219,6 +223,8 @@ label.unsupported.browser.ie=Sorry! current browser is not supported. Please use
|
|||||||
label.auto.assign.description=When an auto assign distribution set is selected, it will be automatically assigned to all targets that match the target filter.
|
label.auto.assign.description=When an auto assign distribution set is selected, it will be automatically assigned to all targets that match the target filter.
|
||||||
label.auto.assign.enable=Enable auto assignment
|
label.auto.assign.enable=Enable auto assignment
|
||||||
label.scheduled=Scheduled
|
label.scheduled=Scheduled
|
||||||
|
label.approval.decision = Approval decision
|
||||||
|
label.approval.remark = Remark (optional)
|
||||||
|
|
||||||
# Checkbox label prefix with - checkbox
|
# Checkbox label prefix with - checkbox
|
||||||
checkbox.dist.migration.required = Required Migration Step :
|
checkbox.dist.migration.required = Required Migration Step :
|
||||||
@@ -254,6 +260,7 @@ tooltip.metadata.icon = Manage Metadata
|
|||||||
tooltip.next.maintenancewindow = next on {0}
|
tooltip.next.maintenancewindow = next on {0}
|
||||||
#rollout action
|
#rollout action
|
||||||
tooltip.rollout.run = Run
|
tooltip.rollout.run = Run
|
||||||
|
tooltip.rollout.approve = Approve
|
||||||
tooltip.rollout.pause = Pause
|
tooltip.rollout.pause = Pause
|
||||||
tooltip.rollout.update = Update..
|
tooltip.rollout.update = Update..
|
||||||
tooltip.rollout.copy = Copy..
|
tooltip.rollout.copy = Copy..
|
||||||
@@ -483,6 +490,8 @@ configuration.polling.title=Polling Configuration
|
|||||||
configuration.polling.time=Polling Time
|
configuration.polling.time=Polling Time
|
||||||
configuration.polling.overduetime=Polling Overdue Time
|
configuration.polling.overduetime=Polling Overdue Time
|
||||||
configuration.polling.custom.value=use a custom value
|
configuration.polling.custom.value=use a custom value
|
||||||
|
configuration.rollout.title=Rollout Configuration
|
||||||
|
configuration.rollout.approval.label=Approve rollout before it can be started
|
||||||
|
|
||||||
#Calendar
|
#Calendar
|
||||||
calendar.year=year
|
calendar.year=year
|
||||||
@@ -511,13 +520,15 @@ header.assigned.ds = Assigned DS
|
|||||||
header.installed.ds = Installed DS
|
header.installed.ds = Installed DS
|
||||||
header.target.status = Status
|
header.target.status = Status
|
||||||
header.target.tags = Tags
|
header.target.tags = Tags
|
||||||
header.total.targets = Targets
|
header.total.targets = Targets
|
||||||
header.key = Key
|
header.key = Key
|
||||||
header.value = Value
|
header.value = Value
|
||||||
metadata.targetvisible = Visible for targets
|
metadata.targetvisible = Visible for targets
|
||||||
header.auto.assignment.ds = Auto assignment
|
header.auto.assignment.ds = Auto assignment
|
||||||
header.target.filter.name = Target filter name
|
header.target.filter.name = Target filter name
|
||||||
header.target.filter.query = Target filter query
|
header.target.filter.query = Target filter query
|
||||||
|
header.approvalDecidedBy = Decided By
|
||||||
|
header.approvalRemark = Approval Remark
|
||||||
|
|
||||||
distribution.details.header = Distribution set
|
distribution.details.header = Distribution set
|
||||||
target.details.header = Target
|
target.details.header = Target
|
||||||
@@ -556,6 +567,7 @@ caption.configure.rollout = Configure Rollout
|
|||||||
caption.configure.rollout.groups = Configure Deployment Groups
|
caption.configure.rollout.groups = Configure Deployment Groups
|
||||||
caption.update.rollout = Update Rollout
|
caption.update.rollout = Update Rollout
|
||||||
caption.create.rollout = Create new Rollout
|
caption.create.rollout = Create new Rollout
|
||||||
|
caption.approve.rollout = Approve Rollout
|
||||||
prompt.target.filter = Custom Target Filter
|
prompt.target.filter = Custom Target Filter
|
||||||
message.rollout.nonzero.group.number = Number of groups must be greater than zero
|
message.rollout.nonzero.group.number = Number of groups must be greater than zero
|
||||||
message.rollout.max.group.number = Number of groups must not be greater than 500
|
message.rollout.max.group.number = Number of groups must not be greater than 500
|
||||||
|
|||||||
Reference in New Issue
Block a user