Optional support for autoclose of deprecated actions (#585)

* Introduce action autoclose into repository.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Extended test.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Fix typo.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Added checkbox to management ui.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Fix Ui allignment.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Fix item ID.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Fix sonar issues.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* readibility.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2017-10-10 17:26:43 +02:00
committed by GitHub
parent c3908c9c34
commit 8e890424c0
26 changed files with 392 additions and 51 deletions

View File

@@ -221,7 +221,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
@PathVariable("controllerId") final String controllerId, @PathVariable("controllerId") final String controllerId,
@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("softwareModuleId") final Long softwareModuleId,
@PathVariable("fileName") final String fileName) { @PathVariable("fileName") final String fileName) {
controllerManagement.getByControllerId(controllerId) final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId)); .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final SoftwareModule module = controllerManagement.getSoftwareModule(softwareModuleId) final SoftwareModule module = controllerManagement.getSoftwareModule(softwareModuleId)
@@ -235,6 +235,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
final Artifact artifact = module.getArtifactByFilename(fileName) final Artifact artifact = module.getArtifactByFilename(fileName)
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, fileName)); .orElseThrow(() -> new EntityNotFoundException(Artifact.class, fileName));
checkAndLogDownload(requestResponseContextHolder.getHttpServletRequest(), target, module.getId());
try { try {
FileStreamingUtil.writeMD5FileResponse(requestResponseContextHolder.getHttpServletResponse(), FileStreamingUtil.writeMD5FileResponse(requestResponseContextHolder.getHttpServletResponse(),
artifact.getMd5Hash(), fileName); artifact.getMd5Hash(), fileName);

View File

@@ -208,10 +208,12 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// create ds // create ds
final DistributionSet ds = testdataFactory.createDistributionSet(""); final DistributionSet ds = testdataFactory.createDistributionSet("");
assignDistributionSet(ds, target);
// create artifact // create artifact
final byte random[] = RandomUtils.nextBytes(5 * 1024); final byte random[] = RandomUtils.nextBytes(5 * 1024);
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "file1",
"file1", false); false);
// download // download
final MvcResult result = mvc final MvcResult result = mvc
@@ -242,8 +244,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
// create artifact // create artifact
final byte random[] = RandomUtils.nextBytes(resultLength); final byte random[] = RandomUtils.nextBytes(resultLength);
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "file1",
"file1", false); false);
assertThat(random.length).isEqualTo(resultLength); assertThat(random.length).isEqualTo(resultLength);

View File

@@ -102,6 +102,12 @@ 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";
/**
* Repository on autoclose mode instead of canceling in case of new DS
* assignment over active actions.
*/
public static final String REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED = "repository.actions.autoclose.enabled";
private String keyName; private String keyName;
private String defaultValue = ""; private String defaultValue = "";
private Class<?> dataType = String.class; private Class<?> dataType = String.class;

View File

@@ -20,6 +20,11 @@ hawkbit.server.tenant.configuration.authentication-header-enabled.defaultValue=$
hawkbit.server.tenant.configuration.authentication-header-enabled.dataType=java.lang.Boolean hawkbit.server.tenant.configuration.authentication-header-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.authentication-header-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator hawkbit.server.tenant.configuration.authentication-header-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
hawkbit.server.tenant.configuration.repository-actions-autoclose-enabled.keyName=repository.actions.autoclose.enabled
hawkbit.server.tenant.configuration.repository-actions-autoclose-enabled.defaultValue=false
hawkbit.server.tenant.configuration.repository-actions-autoclose-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.repository-actions-autoclose-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
hawkbit.server.tenant.configuration.authentication-header-authority.keyName=authentication.header.authority hawkbit.server.tenant.configuration.authentication-header-authority.keyName=authentication.header.authority
hawkbit.server.tenant.configuration.authentication-header-authority.defaultValue=${hawkbit.server.ddi.security.authentication.header.authority} hawkbit.server.tenant.configuration.authentication-header-authority.defaultValue=${hawkbit.server.ddi.security.authentication.header.authority}

View File

@@ -92,7 +92,19 @@ public abstract class AbstractDsAssignmentStrategy {
* to cancel actions for * to cancel actions for
* @return {@link Set} of {@link Target#getId()}s * @return {@link Set} of {@link Target#getId()}s
*/ */
abstract Set<Long> findTargetIdsToCancel(List<List<Long>> targetIds); abstract Set<Long> cancelActiveActions(List<List<Long>> targetIds);
/**
* Cancels actions that can be canceled (i.e.
* {@link DistributionSet#isRequiredMigrationStep() is <code>false</code>})
* as a result of the new assignment and returns all {@link Target}s where
* such actions existed.
*
* @param targetIds
* to cancel actions for
* @return {@link Set} of {@link Target#getId()}s
*/
abstract void closeActiveActions(List<List<Long>> targetIds);
/** /**
* Handles event sending related to the assignment. * Handles event sending related to the assignment.
@@ -119,7 +131,7 @@ public abstract class AbstractDsAssignmentStrategy {
} }
/** /**
* Removes {@link Action}s that are no longer necessary and sends * Cancels {@link Action}s that are no longer necessary and sends
* cancellations to the controller. * cancellations to the controller.
* *
* @param targetsIds * @param targetsIds
@@ -130,8 +142,8 @@ public abstract class AbstractDsAssignmentStrategy {
// Figure out if there are potential target/action combinations that // Figure out if there are potential target/action combinations that
// need to be considered for cancellation // need to be considered for cancellation
final List<JpaAction> activeActions = actionRepository final List<JpaAction> activeActions = actionRepository
.findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(targetsIds, .findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetNotRequiredMigrationStep(
Action.Status.CANCELING); targetsIds, Action.Status.CANCELING);
return activeActions.stream().map(action -> { return activeActions.stream().map(action -> {
action.setStatus(Status.CANCELING); action.setStatus(Status.CANCELING);
@@ -148,6 +160,34 @@ public abstract class AbstractDsAssignmentStrategy {
} }
/**
* Closes {@link Action}s that are no longer necessary without sending a
* hint to the controller.
*
* @param targetsIds
* to override {@link Action}s
*/
protected List<Long> closeObsoleteUpdateActions(final Collection<Long> targetsIds) {
// Figure out if there are potential target/action combinations that
// need to be considered for cancellation
final List<JpaAction> activeActions = actionRepository
.findByActiveAndTargetIdInAndDistributionSetNotRequiredMigrationStep(targetsIds);
return activeActions.stream().map(action -> {
action.setStatus(Status.CANCELED);
action.setActive(false);
// document that the status has been retrieved
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELED, System.currentTimeMillis(),
RepositoryConstants.SERVER_MESSAGE_PREFIX + "close obsolete action due to new update"));
actionRepository.save(action);
return action.getTarget().getId();
}).collect(Collectors.toList());
}
/** /**
* Sends the {@link CancelTargetAssignmentEvent} for a specific target to * Sends the {@link CancelTargetAssignmentEvent} for a specific target to
* the eventPublisher. * the eventPublisher.

View File

@@ -219,9 +219,22 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
*/ */
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD) @EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
@Query("SELECT a FROM JpaAction a WHERE a.active = true AND a.distributionSet.requiredMigrationStep = false AND a.target IN ?1 AND a.status != ?2") @Query("SELECT a FROM JpaAction a WHERE a.active = true AND a.distributionSet.requiredMigrationStep = false AND a.target IN ?1 AND a.status != ?2")
List<JpaAction> findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep( List<JpaAction> findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetNotRequiredMigrationStep(
Collection<Long> targetIds, Action.Status notStatus); Collection<Long> targetIds, Action.Status notStatus);
/**
*
* Retrieves all {@link Action}s which are active and referring to the given
* target Ids and distribution set required migration step.
*
* @param targetIds
* the IDs of targets for the actions
* @return the found list of {@link Action}s
*/
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
@Query("SELECT a FROM JpaAction a WHERE a.active = true AND a.distributionSet.requiredMigrationStep = false AND a.target IN ?1")
List<JpaAction> findByActiveAndTargetIdInAndDistributionSetNotRequiredMigrationStep(Collection<Long> targetIds);
/** /**
* Counts all {@link Action}s referring to the given target. * Counts all {@link Action}s referring to the given target.
* *

View File

@@ -30,6 +30,7 @@ import org.eclipse.hawkbit.repository.ActionFields;
import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException; import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -57,6 +58,8 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@@ -109,13 +112,17 @@ public class JpaDeploymentManagement implements DeploymentManagement {
private final PlatformTransactionManager txManager; private final PlatformTransactionManager txManager;
private final OnlineDsAssignmentStrategy onlineDsAssignmentStrategy; private final OnlineDsAssignmentStrategy onlineDsAssignmentStrategy;
private final OfflineDsAssignmentStrategy offlineDsAssignmentStrategy; private final OfflineDsAssignmentStrategy offlineDsAssignmentStrategy;
private final TenantConfigurationManagement tenantConfigurationManagement;
private final SystemSecurityContext systemSecurityContext;
JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository, JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository,
final DistributionSetRepository distributionSetRepository, final TargetRepository targetRepository, final DistributionSetRepository distributionSetRepository, final TargetRepository targetRepository,
final ActionStatusRepository actionStatusRepository, final TargetManagement targetManagement, final ActionStatusRepository actionStatusRepository, final TargetManagement targetManagement,
final AuditorAware<String> auditorProvider, final ApplicationEventPublisher eventPublisher, final AuditorAware<String> auditorProvider, final ApplicationEventPublisher eventPublisher,
final ApplicationContext applicationContext, final AfterTransactionCommitExecutor afterCommit, final ApplicationContext applicationContext, final AfterTransactionCommitExecutor afterCommit,
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager) { final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
final TenantConfigurationManagement tenantConfigurationManagement,
final SystemSecurityContext systemSecurityContext) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.actionRepository = actionRepository; this.actionRepository = actionRepository;
this.distributionSetRepository = distributionSetRepository; this.distributionSetRepository = distributionSetRepository;
@@ -132,6 +139,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
applicationContext, actionRepository, actionStatusRepository); applicationContext, actionRepository, actionStatusRepository);
offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher, offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher,
applicationContext, actionRepository, actionStatusRepository); applicationContext, actionRepository, actionStatusRepository);
this.tenantConfigurationManagement = tenantConfigurationManagement;
this.systemSecurityContext = systemSecurityContext;
} }
@Override @Override
@@ -254,7 +263,16 @@ public class JpaDeploymentManagement implements DeploymentManagement {
// need to remember which one we have been switched to canceling state // need to remember which one we have been switched to canceling state
// because for targets which we have changed to canceling we don't want // because for targets which we have changed to canceling we don't want
// to publish the new action update event. // to publish the new action update event.
final Set<Long> targetIdsCancellList = assignmentStrategy.findTargetIdsToCancel(targetIds); final Set<Long> targetIdsCancellList;
if (systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
.getConfigurationValue(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, Boolean.class)
.getValue())) {
targetIdsCancellList = Collections.emptySet();
assignmentStrategy.closeActiveActions(targetIds);
} else {
targetIdsCancellList = assignmentStrategy.cancelActiveActions(targetIds);
}
// cancel all scheduled actions which are in-active, these actions were // cancel all scheduled actions which are in-active, these actions were
// not active before and the manual assignment which has been done // not active before and the manual assignment which has been done

View File

@@ -85,8 +85,9 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Long targetFilterQueryId) { public void delete(final Long targetFilterQueryId) {
get(targetFilterQueryId) if (!targetFilterQueryRepository.exists(targetFilterQueryId)) {
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId)); throw new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId);
}
targetFilterQueryRepository.delete(targetFilterQueryId); targetFilterQueryRepository.delete(targetFilterQueryId);
} }

View File

@@ -44,7 +44,7 @@ public class JpaTenantStatsManagement implements TenantStatsManagement {
result.setTargets(targetRepository.count()); result.setTargets(targetRepository.count());
result.setArtifacts(artifactRepository.countBySoftwareModuleDeleted(false)); result.setArtifacts(artifactRepository.countBySoftwareModuleDeleted(false));
artifactRepository.getSumOfUndeletedArtifactSize().map(result::setOverallArtifactVolumeInBytes); artifactRepository.getSumOfUndeletedArtifactSize().ifPresent(result::setOverallArtifactVolumeInBytes);
result.setActions(actionRepository.count()); result.setActions(actionRepository.count());
return result; return result;

View File

@@ -67,10 +67,15 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
} }
@Override @Override
public Set<Long> findTargetIdsToCancel(final List<List<Long>> targetIds) { public Set<Long> cancelActiveActions(final List<List<Long>> targetIds) {
return Collections.emptySet(); return Collections.emptySet();
} }
@Override
void closeActiveActions(final List<List<Long>> targetIds) {
// Not supported by offline case
}
@Override @Override
void updateTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds, final String currentUser) { void updateTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds, final String currentUser) {
targetIds.forEach(tIds -> targetRepository.setAssignedAndInstalledDistributionSetAndUpdateStatus( targetIds.forEach(tIds -> targetRepository.setAssignedAndInstalledDistributionSetAndUpdateStatus(

View File

@@ -67,11 +67,16 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
} }
@Override @Override
Set<Long> findTargetIdsToCancel(final List<List<Long>> targetIds) { Set<Long> cancelActiveActions(final List<List<Long>> targetIds) {
return targetIds.stream().map(this::overrideObsoleteUpdateActions).flatMap(Collection::stream) return targetIds.stream().map(this::overrideObsoleteUpdateActions).flatMap(Collection::stream)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
@Override
void closeActiveActions(final List<List<Long>> targetIds) {
targetIds.forEach(this::closeObsoleteUpdateActions);
}
@Override @Override
void updateTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds, final String currentUser) { void updateTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds, final String currentUser) {
targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSetAndUpdateStatus(TargetUpdateStatus.PENDING, targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSetAndUpdateStatus(TargetUpdateStatus.PENDING,

View File

@@ -531,10 +531,12 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final TargetManagement targetManagement, final AuditorAware<String> auditorProvider, final TargetManagement targetManagement, final AuditorAware<String> auditorProvider,
final ApplicationEventPublisher eventPublisher, final ApplicationContext applicationContext, final ApplicationEventPublisher eventPublisher, final ApplicationContext applicationContext,
final AfterTransactionCommitExecutor afterCommit, final VirtualPropertyReplacer virtualPropertyReplacer, final AfterTransactionCommitExecutor afterCommit, final VirtualPropertyReplacer virtualPropertyReplacer,
final PlatformTransactionManager txManager) { final PlatformTransactionManager txManager,
final TenantConfigurationManagement tenantConfigurationManagement,
final SystemSecurityContext systemSecurityContext) {
return new JpaDeploymentManagement(entityManager, actionRepository, distributionSetRepository, targetRepository, return new JpaDeploymentManagement(entityManager, actionRepository, distributionSetRepository, targetRepository,
actionStatusRepository, targetManagement, auditorProvider, eventPublisher, applicationContext, actionStatusRepository, targetManagement, auditorProvider, eventPublisher, applicationContext,
afterCommit, virtualPropertyReplacer, txManager); afterCommit, virtualPropertyReplacer, txManager, tenantConfigurationManagement, systemSecurityContext);
} }
/** /**

View File

@@ -272,7 +272,7 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
if (allready >= softwareModule.getType().getMaxAssignments()) { if (allready >= softwareModule.getType().getMaxAssignments()) {
modules.stream().filter(module -> module.getType().getKey().equals(softwareModule.getType().getKey())) modules.stream().filter(module -> module.getType().getKey().equals(softwareModule.getType().getKey()))
.findAny().map(modules::remove); .findAny().ifPresent(modules::remove);
} }
if (modules.add(softwareModule)) { if (modules.add(softwareModule)) {

View File

@@ -55,6 +55,7 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.eclipse.hawkbit.repository.model.TargetWithActionType;
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.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -460,6 +461,52 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
.allMatch(target -> target.getLastModifiedAt().equals(target.getInstallationDate())); .allMatch(target -> target.getLastModifiedAt().equals(target.getInstallationDate()));
} }
@Test
@Description("Verifies that if an account is set to action autoclose running actions in case of a new assigned set get closed and set to CANCELED.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 10),
@Expect(type = TargetUpdatedEvent.class, count = 20), @Expect(type = ActionCreatedEvent.class, count = 20),
@Expect(type = ActionUpdatedEvent.class, count = 10),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 20) })
public void assigneDistributionSetAndAutoCloseActiveActions() {
tenantConfigurationManagement
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, true);
try {
final List<Target> targets = testdataFactory.createTargets(10);
// First assignment
final DistributionSet ds1 = testdataFactory.createDistributionSet("1");
assignDistributionSet(ds1, targets);
List<Action> assignmentOne = actionRepository.findByDistributionSetId(PAGE, ds1.getId()).getContent();
assertThat(assignmentOne).hasSize(10).as("Is active").allMatch(Action::isActive).as("Is assigned DS")
.allMatch(action -> action.getDistributionSet().getId() == ds1.getId()).as("Is running")
.allMatch(action -> action.getStatus() == Status.RUNNING);
// Second assignment
final DistributionSet ds2 = testdataFactory.createDistributionSet("2");
assignDistributionSet(ds2, targets);
final List<Action> assignmentTwo = actionRepository.findByDistributionSetId(PAGE, ds2.getId()).getContent();
assignmentOne = actionRepository.findByDistributionSetId(PAGE, ds1.getId()).getContent();
assertThat(assignmentTwo).hasSize(10).as("Is active").allMatch(Action::isActive).as("Is assigned DS")
.allMatch(action -> action.getDistributionSet().getId() == ds2.getId()).as("Is running")
.allMatch(action -> action.getStatus() == Status.RUNNING);
assertThat(assignmentOne).hasSize(10).as("Is active").allMatch(action -> !action.isActive())
.as("Is assigned to DS").allMatch(action -> action.getDistributionSet().getId() == ds1.getId())
.as("Is cancelled").allMatch(action -> action.getStatus() == Status.CANCELED);
assertThat(targetManagement.findByAssignedDistributionSet(PAGE, ds2.getId()).getContent()).hasSize(10)
.as("InstallationDate not set").allMatch(target -> (target.getInstallationDate() == null));
} finally {
tenantConfigurationManagement
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, false);
}
}
/** /**
* test a simple deployment by calling the * test a simple deployment by calling the
* {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)} * {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)}

View File

@@ -12,10 +12,10 @@ import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.security.SecurityTokenGenerator; import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.tenantconfiguration.authentication.AnonymousDownloadAuthenticationConfigurationItem; import org.eclipse.hawkbit.ui.tenantconfiguration.authentication.AnonymousDownloadAuthenticationConfigurationItem;
import org.eclipse.hawkbit.ui.tenantconfiguration.authentication.AuthenticationConfigurationItem;
import org.eclipse.hawkbit.ui.tenantconfiguration.authentication.CertificateAuthenticationConfigurationItem; import org.eclipse.hawkbit.ui.tenantconfiguration.authentication.CertificateAuthenticationConfigurationItem;
import org.eclipse.hawkbit.ui.tenantconfiguration.authentication.GatewaySecurityTokenAuthenticationConfigurationItem; import org.eclipse.hawkbit.ui.tenantconfiguration.authentication.GatewaySecurityTokenAuthenticationConfigurationItem;
import org.eclipse.hawkbit.ui.tenantconfiguration.authentication.TargetSecurityTokenAuthenticationConfigurationItem; import org.eclipse.hawkbit.ui.tenantconfiguration.authentication.TargetSecurityTokenAuthenticationConfigurationItem;
import org.eclipse.hawkbit.ui.tenantconfiguration.generic.BooleanConfigurationItem;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
@@ -31,7 +31,7 @@ import com.vaadin.ui.VerticalLayout;
* View to configure the authentication mode. * View to configure the authentication mode.
*/ */
public class AuthenticationConfigurationView extends BaseConfigurationView public class AuthenticationConfigurationView extends BaseConfigurationView
implements ConfigurationGroup, ConfigurationItem.ConfigurationItemChangeListener, ValueChangeListener { implements ConfigurationItem.ConfigurationItemChangeListener, ValueChangeListener {
private static final String DIST_CHECKBOX_STYLE = "dist-checkbox-style"; private static final String DIST_CHECKBOX_STYLE = "dist-checkbox-style";
@@ -55,7 +55,8 @@ public class AuthenticationConfigurationView extends BaseConfigurationView
private CheckBox downloadAnonymousCheckBox; private CheckBox downloadAnonymousCheckBox;
AuthenticationConfigurationView(final VaadinMessageSource i18n, final TenantConfigurationManagement tenantConfigurationManagement, AuthenticationConfigurationView(final VaadinMessageSource i18n,
final TenantConfigurationManagement tenantConfigurationManagement,
final SecurityTokenGenerator securityTokenGenerator) { final SecurityTokenGenerator securityTokenGenerator) {
this.i18n = i18n; this.i18n = i18n;
this.certificateAuthenticationConfigurationItem = new CertificateAuthenticationConfigurationItem( this.certificateAuthenticationConfigurationItem = new CertificateAuthenticationConfigurationItem(
@@ -161,7 +162,7 @@ public class AuthenticationConfigurationView extends BaseConfigurationView
notifyConfigurationChanged(); notifyConfigurationChanged();
final CheckBox checkBox = (CheckBox) event.getProperty(); final CheckBox checkBox = (CheckBox) event.getProperty();
AuthenticationConfigurationItem configurationItem; BooleanConfigurationItem configurationItem;
if (gatewaySecTokenCheckBox.equals(checkBox)) { if (gatewaySecTokenCheckBox.equals(checkBox)) {
configurationItem = gatewaySecurityTokenAuthenticationConfigurationItem; configurationItem = gatewaySecurityTokenAuthenticationConfigurationItem;

View File

@@ -0,0 +1,125 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.tenantconfiguration;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.tenantconfiguration.generic.BooleanConfigurationItem;
import org.eclipse.hawkbit.ui.tenantconfiguration.repository.ActionAutocloseConfigurationItem;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;
/**
* View to configure the authentication mode.
*/
public class RepositoryConfigurationView extends BaseConfigurationView
implements ConfigurationGroup, ConfigurationItem.ConfigurationItemChangeListener, ValueChangeListener {
private static final String DIST_CHECKBOX_STYLE = "dist-checkbox-style";
private static final long serialVersionUID = 1L;
private final VaadinMessageSource i18n;
private final ActionAutocloseConfigurationItem actionAutocloseConfigurationItem;
private CheckBox actionAutocloseCheckBox;
RepositoryConfigurationView(final VaadinMessageSource i18n,
final TenantConfigurationManagement tenantConfigurationManagement) {
this.i18n = i18n;
this.actionAutocloseConfigurationItem = new ActionAutocloseConfigurationItem(tenantConfigurationManagement,
i18n);
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.repository.title"));
headerDisSetType.addStyleName("config-panel-header");
vLayout.addComponent(headerDisSetType);
final GridLayout gridLayout = new GridLayout(2, 1);
gridLayout.setSpacing(true);
gridLayout.setImmediate(true);
gridLayout.setColumnExpandRatio(1, 1.0F);
gridLayout.setSizeFull();
actionAutocloseCheckBox = SPUIComponentProvider.getCheckBox("", DIST_CHECKBOX_STYLE, null, false, "");
actionAutocloseCheckBox.setId(UIComponentIdProvider.REPOSITORY_ACTIONS_AUTOCLOSE_CHECKBOX);
actionAutocloseCheckBox.setValue(actionAutocloseConfigurationItem.isConfigEnabled());
actionAutocloseCheckBox.addValueChangeListener(this);
actionAutocloseConfigurationItem.addChangeListener(this);
gridLayout.addComponent(actionAutocloseCheckBox, 0, 0);
gridLayout.addComponent(actionAutocloseConfigurationItem, 1, 0);
vLayout.addComponent(gridLayout);
rootPanel.setContent(vLayout);
setCompositionRoot(rootPanel);
}
@Override
public void save() {
actionAutocloseConfigurationItem.save();
}
@Override
public void undo() {
actionAutocloseConfigurationItem.undo();
actionAutocloseCheckBox.setValue(actionAutocloseConfigurationItem.isConfigEnabled());
}
@Override
public void configurationHasChanged() {
notifyConfigurationChanged();
}
@Override
public void valueChange(final ValueChangeEvent event) {
if (!(event.getProperty() instanceof CheckBox)) {
return;
}
notifyConfigurationChanged();
final CheckBox checkBox = (CheckBox) event.getProperty();
BooleanConfigurationItem configurationItem;
if (actionAutocloseCheckBox.equals(checkBox)) {
configurationItem = actionAutocloseConfigurationItem;
} else {
return;
}
if (checkBox.getValue()) {
configurationItem.configEnable();
} else {
configurationItem.configDisable();
}
}
}

View File

@@ -56,6 +56,8 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
private final DefaultDistributionSetTypeLayout defaultDistributionSetTypeLayout; private final DefaultDistributionSetTypeLayout defaultDistributionSetTypeLayout;
private final RepositoryConfigurationView repositoryConfigurationView;
private final AuthenticationConfigurationView authenticationConfigurationView; private final AuthenticationConfigurationView authenticationConfigurationView;
private final PollingConfigurationView pollingConfigurationView; private final PollingConfigurationView pollingConfigurationView;
@@ -87,6 +89,8 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
securityTokenGenerator); securityTokenGenerator);
this.pollingConfigurationView = new PollingConfigurationView(i18n, controllerPollProperties, this.pollingConfigurationView = new PollingConfigurationView(i18n, controllerPollProperties,
tenantConfigurationManagement); tenantConfigurationManagement);
this.repositoryConfigurationView = new RepositoryConfigurationView(i18n, tenantConfigurationManagement);
this.i18n = i18n; this.i18n = i18n;
this.uiProperties = uiProperties; this.uiProperties = uiProperties;
this.uINotification = uINotification; this.uINotification = uINotification;
@@ -100,6 +104,8 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
if (defaultDistributionSetTypeLayout.getComponentCount() > 0) { if (defaultDistributionSetTypeLayout.getComponentCount() > 0) {
configurationViews.add(defaultDistributionSetTypeLayout); configurationViews.add(defaultDistributionSetTypeLayout);
} }
configurationViews.add(repositoryConfigurationView);
configurationViews.add(authenticationConfigurationView); configurationViews.add(authenticationConfigurationView);
configurationViews.add(pollingConfigurationView); configurationViews.add(pollingConfigurationView);
if (customConfigurationViews != null) { if (customConfigurationViews != null) {

View File

@@ -10,13 +10,14 @@ package org.eclipse.hawkbit.ui.tenantconfiguration.authentication;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.ui.tenantconfiguration.generic.AbstractBooleanTenantConfigurationItem;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
/** /**
* This class represents the UI item for the anonymous download by in the * This class represents the UI item for the anonymous download by in the
* authentication configuration view. * authentication configuration view.
*/ */
public class AnonymousDownloadAuthenticationConfigurationItem extends AbstractAuthenticationTenantConfigurationItem { public class AnonymousDownloadAuthenticationConfigurationItem extends AbstractBooleanTenantConfigurationItem {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -12,6 +12,7 @@ import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.ui.common.builder.LabelBuilder; import org.eclipse.hawkbit.ui.common.builder.LabelBuilder;
import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder; import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder;
import org.eclipse.hawkbit.ui.tenantconfiguration.generic.AbstractBooleanTenantConfigurationItem;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.HorizontalLayout;
@@ -23,7 +24,7 @@ import com.vaadin.ui.VerticalLayout;
* This class represents the UI item for the certificate authenticated by an * This class represents the UI item for the certificate authenticated by an
* reverse proxy in the authentication configuration view. * reverse proxy in the authentication configuration view.
*/ */
public class CertificateAuthenticationConfigurationItem extends AbstractAuthenticationTenantConfigurationItem { public class CertificateAuthenticationConfigurationItem extends AbstractBooleanTenantConfigurationItem {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -15,6 +15,7 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.T
import org.eclipse.hawkbit.ui.common.builder.LabelBuilder; import org.eclipse.hawkbit.ui.common.builder.LabelBuilder;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmall; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmall;
import org.eclipse.hawkbit.ui.tenantconfiguration.generic.AbstractBooleanTenantConfigurationItem;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import com.vaadin.server.FontAwesome; import com.vaadin.server.FontAwesome;
@@ -28,7 +29,7 @@ import com.vaadin.ui.themes.ValoTheme;
* This class represents the UI item for the gateway security token section in * This class represents the UI item for the gateway security token section in
* the authentication configuration view. * the authentication configuration view.
*/ */
public class GatewaySecurityTokenAuthenticationConfigurationItem extends AbstractAuthenticationTenantConfigurationItem { public class GatewaySecurityTokenAuthenticationConfigurationItem extends AbstractBooleanTenantConfigurationItem {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -10,13 +10,14 @@ package org.eclipse.hawkbit.ui.tenantconfiguration.authentication;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.ui.tenantconfiguration.generic.AbstractBooleanTenantConfigurationItem;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
/** /**
* This class represents the UI item for the target security token section in * This class represents the UI item for the target security token section in
* the authentication configuration view. * the authentication configuration view.
*/ */
public class TargetSecurityTokenAuthenticationConfigurationItem extends AbstractAuthenticationTenantConfigurationItem { public class TargetSecurityTokenAuthenticationConfigurationItem extends AbstractBooleanTenantConfigurationItem {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.ui.tenantconfiguration.authentication; package org.eclipse.hawkbit.ui.tenantconfiguration.generic;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -21,8 +21,8 @@ import com.vaadin.ui.VerticalLayout;
/** /**
* abstract authentication configuration item. * abstract authentication configuration item.
*/ */
abstract class AbstractAuthenticationTenantConfigurationItem extends VerticalLayout public abstract class AbstractBooleanTenantConfigurationItem extends VerticalLayout
implements AuthenticationConfigurationItem { implements BooleanConfigurationItem {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -40,7 +40,7 @@ abstract class AbstractAuthenticationTenantConfigurationItem extends VerticalLay
* the tenant configuration management to retrieve the * the tenant configuration management to retrieve the
* configuration value * configuration value
*/ */
public AbstractAuthenticationTenantConfigurationItem(final String configurationKey, protected AbstractBooleanTenantConfigurationItem(final String configurationKey,
final TenantConfigurationManagement tenantConfigurationManagement, final VaadinMessageSource i18n) { final TenantConfigurationManagement tenantConfigurationManagement, final VaadinMessageSource i18n) {
this.configurationKey = configurationKey; this.configurationKey = configurationKey;
this.tenantConfigurationManagement = tenantConfigurationManagement; this.tenantConfigurationManagement = tenantConfigurationManagement;

View File

@@ -6,21 +6,15 @@
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package org.eclipse.hawkbit.ui.tenantconfiguration.authentication; package org.eclipse.hawkbit.ui.tenantconfiguration.generic;
import org.eclipse.hawkbit.ui.tenantconfiguration.ConfigurationItem; import org.eclipse.hawkbit.ui.tenantconfiguration.ConfigurationGroup;
import com.vaadin.ui.Component;
/** /**
* Interface to be implemented by any tenant specific configuration to show on * Interface to be implemented by any tenant specific configuration to show on
* the UI. * the UI.
*
*
*
*
*/ */
public interface AuthenticationConfigurationItem extends Component, ConfigurationItem { public interface BooleanConfigurationItem extends ConfigurationGroup {
/** /**
* @return {@code true} if configuration is enabled, otherwise {@code false} * @return {@code true} if configuration is enabled, otherwise {@code false}
@@ -37,14 +31,4 @@ public interface AuthenticationConfigurationItem extends Component, Configuratio
*/ */
void configDisable(); void configDisable();
/**
* called to save the configuration.
*/
void save();
/**
* called to rollback made changes.
*/
void undo();
} }

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.tenantconfiguration.repository;
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 ActionAutocloseConfigurationItem extends AbstractBooleanTenantConfigurationItem {
private static final long serialVersionUID = 1L;
private boolean configurationEnabled;
private boolean configurationEnabledChange;
public ActionAutocloseConfigurationItem(final TenantConfigurationManagement tenantConfigurationManagement,
final VaadinMessageSource i18n) {
super(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, tenantConfigurationManagement, i18n);
super.init("label.configuration.repository.autoclose.action");
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();
}
}

View File

@@ -9,6 +9,8 @@
package org.eclipse.hawkbit.ui.utils; package org.eclipse.hawkbit.ui.utils;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
/** /**
* Interface to provide the unchanged constants. * Interface to provide the unchanged constants.
*/ */
@@ -1039,6 +1041,12 @@ public final class UIComponentIdProvider {
*/ */
public static final String ARTIFACT_DETAILS_HEADER_LABEL_ID = "artifact.details.header.caption"; public static final String ARTIFACT_DETAILS_HEADER_LABEL_ID = "artifact.details.header.caption";
/**
* Configuration checkbox for
* {@link TenantConfigurationKey#REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED}.
*/
public static final String REPOSITORY_ACTIONS_AUTOCLOSE_CHECKBOX = "repositoryactionsautoclosecheckbox";
/** /**
* /* Private Constructor. * /* Private Constructor.
*/ */

View File

@@ -194,6 +194,7 @@ label.tag.name = Tag name
label.configuration.auth.header = Allow targets to authenticate via a certificate authenticated by an reverse proxy label.configuration.auth.header = Allow targets to authenticate via a certificate authenticated by an reverse proxy
label.configuration.auth.gatewaytoken = Allow a gateway to authenticate and manage multiple targets through a gateway security token label.configuration.auth.gatewaytoken = Allow a gateway to authenticate and manage multiple targets through a gateway security token
label.configuration.auth.targettoken = Allow targets to authenticate directly with their target security token label.configuration.auth.targettoken = Allow targets to authenticate directly with their target security token
label.configuration.repository.autoclose.action = Autoclose running actions with new Distribution Set assignment
label.configuration.anonymous.download = Allow targets to download artifacts without security credentials label.configuration.anonymous.download = Allow targets to download artifacts without security credentials
label.unsupported.browser.ie=Sorry! current browser is not supported. Please use Internet Explorer 11 and above label.unsupported.browser.ie=Sorry! current browser is not supported. Please use Internet Explorer 11 and above
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.
@@ -450,6 +451,7 @@ configuration.defaultdistributionset.select.label=Select the default Distributio
configuration.savebutton.tooltip=Save Configurations configuration.savebutton.tooltip=Save Configurations
configuration.cancellbutton.tooltip=Cancel Configurations configuration.cancellbutton.tooltip=Cancel Configurations
configuration.authentication.title=Authentication Configuration configuration.authentication.title=Authentication Configuration
configuration.repository.title=Repository Configuration
configuration.polling.title=Polling Configuration 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